./particle/Particle.js
var Shape = require('./Shape')
,   shallowClone = require('./Utils').shallowClone
,   copy = require('./vec2').copy;

module.exports = Particle;

/**
 * Particle shape class.
 * @class Particle
 * @constructor
 * @param {object} [options] (Note that this options object will be passed on to the {{#crossLink "Shape"}}{{/crossLink}} constructor.)
 * @extends Shape
 * @example
 *     var body = new Body();
 *     var shape = new Particle();
 *     body.addShape(shape);
 */
function Particle(options){
    options = options ? shallowClone(options) : {};
	options.type = Shape.PARTICLE;
    Shape.call(this, options);
}
Particle.prototype = new Shape();
Particle.prototype.constructor = Particle;

Particle.prototype.computeMomentOfInertia = function(){
    return 0; // Can't rotate a particle
};

Particle.prototype.updateBoundingRadius = function(){
    this.boundingRadius = 0;
};

/**
 * @method computeAABB
 * @param  {AABB}   out
 * @param  {Array}  position
 * @param  {Number} angle
 */
Particle.prototype.computeAABB = function(out, position/*, angle*/){
    copy(out.lowerBound, position);
    copy(out.upperBound, position);
};

./particle/Shape.js
module.exports = Shape;

var vec2 = require('./vec2');

/**
 * Base class for shapes. Not to be used directly.
 * @class Shape
 * @constructor
 * @param {object} [options]
 * @param {number} [options.angle=0]
 * @param {number} [options.collisionGroup=1]
 * @param {number} [options.collisionMask=1]
 * @param {boolean} [options.collisionResponse=true]
 * @param {Material} [options.material=null]
 * @param {array} [options.position]
 * @param {boolean} [options.sensor=false]
 * @param {object} [options.type=0]
 */
function Shape(options){
    options = options || {};

    /**
     * The body this shape is attached to. A shape can only be attached to a single body.
     * @property {Body} body
     */
    this.body = null;

    /**
     * Body-local position of the shape.
     * @property {Array} position
     */
    this.position = vec2.create();
    if(options.position){
        vec2.copy(this.position, options.position);
    }

    /**
     * Body-local angle of the shape.
     * @property {number} angle
     */
    this.angle = options.angle || 0;

    /**
     * The type of the shape. One of:
     *
     *

     *
Shape.CIRCLE

     *
Shape.PARTICLE

     *
Shape.PLANE

     *
Shape.CONVEX

     *
Shape.LINE

     *
Shape.BOX

     *
Shape.CAPSULE

     *
Shape.HEIGHTFIELD

     *

     *
     * @property {number} type
     */
    this.type = options.type || 0;

    /**
     * Shape object identifier. Read only.
     * @readonly
     * @type {Number}
     * @property id
     */
    this.id = Shape.idCounter++;

    /**
     * Bounding circle radius of this shape
     * @readonly
     * @property boundingRadius
     * @type {Number}
     */
    this.boundingRadius = 0;

    /**
     * Collision group that this shape belongs to (bit mask). See this tutorial.
     * @property collisionGroup
     * @type {Number}
     * @example
     *     // Setup bits for each available group
     *     var PLAYER = Math.pow(2,0),
     *         ENEMY =  Math.pow(2,1),
     *         GROUND = Math.pow(2,2)
     *
     *     // Put shapes into their groups
     *     player1Shape.collisionGroup = PLAYER;
     *     player2Shape.collisionGroup = PLAYER;
     *     enemyShape  .collisionGroup = ENEMY;
     *     groundShape .collisionGroup = GROUND;
     *
     *     // Assign groups that each shape collide with.
     *     // Note that the players can collide with ground and enemies, but not with other players.
     *     player1Shape.collisionMask = ENEMY | GROUND;
     *     player2Shape.collisionMask = ENEMY | GROUND;
     *     enemyShape  .collisionMask = PLAYER | GROUND;
     *     groundShape .collisionMask = PLAYER | ENEMY;
     *
     * @example
     *     // How collision check is done
     *     if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
     *         // The shapes will collide
     *     }
     */
    this.collisionGroup = options.collisionGroup !== undefined ? options.collisionGroup : 1;

    /**
     * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. That means that this shape will move through other body shapes, but it will still trigger contact events, etc.
     * @property {Boolean} collisionResponse
     */
    this.collisionResponse = options.collisionResponse !== undefined ? options.collisionResponse : true;

    /**
     * Collision mask of this shape. See .collisionGroup.
     * @property collisionMask
     * @type {Number}
     */
    this.collisionMask = options.collisionMask !== undefined ? options.collisionMask : 1;

    /**
     * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead.
     * @property material
     * @type {Material}
     */
    this.material = options.material || null;

    /**
     * Area of this shape.
     * @property area
     * @type {Number}
     */
    this.area = 0;

    /**
     * Set to true if you want this shape to be a sensor. A sensor does not generate contacts, but it still reports contact events. This is good if you want to know if a shape is overlapping another shape, without them generating contacts.
     * @property {Boolean} sensor
     */
    this.sensor = options.sensor !== undefined ? options.sensor : false;

    if(this.type){
        this.updateBoundingRadius();
    }

    this.updateArea();
}

Shape.idCounter = 0;

/**
 * @static
 * @property {Number} CIRCLE
 */
Shape.CIRCLE =      1;

/**
 * @static
 * @property {Number} PARTICLE
 */
Shape.PARTICLE =    2;

/**
 * @static
 * @property {Number} PLANE
 */
Shape.PLANE =       4;

/**
 * @static
 * @property {Number} CONVEX
 */
Shape.CONVEX =      8;

/**
 * @static
 * @property {Number} LINE
 */
Shape.LINE =        16;

/**
 * @static
 * @property {Number} BOX
 */
Shape.BOX =   32;

/**
 * @static
 * @property {Number} CAPSULE
 */
Shape.CAPSULE = 64;

/**
 * @static
 * @property {Number} HEIGHTFIELD
 */
Shape.HEIGHTFIELD = 128;

Shape.prototype = {

    /**
     * Should return the moment of inertia around the Z axis of the body. See Wikipedia's list of moments of inertia.
     * @method computeMomentOfInertia
     * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0.
     */
    computeMomentOfInertia: function(){},

    /**
     * Returns the bounding circle radius of this shape.
     * @method updateBoundingRadius
     * @return {Number}
     */
    updateBoundingRadius: function(){},

    /**
     * Update the .area property of the shape.
     * @method updateArea
     */
    updateArea: function(){},

    /**
     * Compute the world axis-aligned bounding box (AABB) of this shape.
     * @method computeAABB
     * @param  {AABB} out The resulting AABB.
     * @param  {Array} position World position of the shape.
     * @param  {Number} angle World angle of the shape.
     */
    computeAABB: function(/*out, position, angle*/){
        // To be implemented in each subclass
    },

    /**
     * Perform raycasting on this shape.
     * @method raycast
     * @param  {RayResult} result Where to store the resulting data.
     * @param  {Ray} ray The Ray that you want to use for raycasting.
     * @param  {array} position World position of the shape (the .position property will be ignored).
     * @param  {number} angle World angle of the shape (the .angle property will be ignored).
     */
    raycast: function(/*result, ray, position, angle*/){
        // To be implemented in each subclass
    },

    /**
     * Test if a point is inside this shape.
     * @method pointTest
     * @param {array} localPoint
     * @return {boolean}
     */
    pointTest: function(/*localPoint*/){ return false; },

    /**
     * Transform a world point to local shape space (assumed the shape is transformed by both itself and the body).
     * @method worldPointToLocal
     * @param {array} out
     * @param {array} worldPoint
     */
    worldPointToLocal: (function () {
        var shapeWorldPosition = vec2.create();
        return function (out, worldPoint) {
            var body = this.body;

            vec2.rotate(shapeWorldPosition, this.position, body.angle);
            vec2.add(shapeWorldPosition, shapeWorldPosition, body.position);

            vec2.toLocalFrame(out, worldPoint, shapeWorldPosition, this.body.angle + this.angle);
        };
    })()
};

./particle/Utils.js
/* global P2_ARRAY_TYPE */

module.exports = Utils;

/**
 * Misc utility functions
 * @class Utils
 * @constructor
 */
function Utils(){}

/**
 * Append the values in array b to the array a. See this for an explanation.
 * @method appendArray
 * @static
 * @param  {Array} a
 * @param  {Array} b
 */
Utils.appendArray = function(a,b){
    if (b.length < 150000) {
        a.push.apply(a, b);
    } else {
        for (var i = 0, len = b.length; i !== len; ++i) {
            a.push(b[i]);
        }
    }
};

/**
 * Garbage free Array.splice(). Does not allocate a new array.
 * @method splice
 * @static
 * @param  {Array} array
 * @param  {Number} index
 * @param  {Number} howmany
 */
Utils.splice = function(array,index,howmany){
    howmany = howmany || 1;
    for (var i=index, len=array.length-howmany; i < len; i++){
        array[i] = array[i + howmany];
    }
    array.length = len;
};

/**
 * Remove an element from an array, if the array contains the element.
 * @method arrayRemove
 * @static
 * @param  {Array} array
 * @param  {Number} element
 */
Utils.arrayRemove = function(array, element){
    var idx = array.indexOf(element);
    if(idx!==-1){
        Utils.splice(array, idx, 1);
    }
};

/**
 * The array type to use for internal numeric computations throughout the library. Float32Array is used if it is available, but falls back on Array. If you want to set array type manually, inject it via the global variable P2_ARRAY_TYPE. See example below.
 * @static
 * @property {function} ARRAY_TYPE
 * @example
 *
 *
 */
if(typeof P2_ARRAY_TYPE !== 'undefined') {
    Utils.ARRAY_TYPE = P2_ARRAY_TYPE;
} else if (typeof Float32Array !== 'undefined'){
    Utils.ARRAY_TYPE = Float32Array;
} else {
    Utils.ARRAY_TYPE = Array;
}

/**
 * Extend an object with the properties of another
 * @static
 * @method extend
 * @param  {object} a
 * @param  {object} b
 */
Utils.extend = function(a,b){
    for(var key in b){
        a[key] = b[key];
    }
};

/**
 * Shallow clone an object. Returns a new object instance with the same properties as the input instance.
 * @static
 * @method shallowClone
 * @param  {object} obj
 */
Utils.shallowClone = function(obj){
    var newObj = {};
    Utils.extend(newObj, obj);
    return newObj;
};

/**
 * Extend an options object with default values.
 * @deprecated Not used internally, will be removed.
 * @static
 * @method defaults
 * @param  {object} options The options object. May be falsy: in this case, a new object is created and returned.
 * @param  {object} defaults An object containing default values.
 * @return {object} The modified options object.
 */
Utils.defaults = function(options, defaults){
    console.warn('Utils.defaults is deprecated.');
    options = options || {};
    for(var key in defaults){
        if(!(key in options)){
            options[key] = defaults[key];
        }
    }
    return options;
};

./particle/vec2.js
/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

/**
 * The vec2 object from glMatrix, with some extensions and some removed methods. See http://glmatrix.net.
 * @class vec2
 */

var vec2 = module.exports = {};

var Utils = require('./Utils');

/**
 * Make a cross product and only return the z component
 * @method crossLength
 * @static
 * @param  {Array} a
 * @param  {Array} b
 * @return {Number}
 */
vec2.crossLength = function(a,b){
    return a[0] * b[1] - a[1] * b[0];
};

/**
 * Cross product between a vector and the Z component of a vector
 * @method crossVZ
 * @static
 * @param  {Array} out
 * @param  {Array} vec
 * @param  {Number} zcomp
 * @return {Array}
 */
vec2.crossVZ = function(out, vec, zcomp){
    vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule
    vec2.scale(out,out,zcomp);      // Scale with z
    return out;
};

/**
 * Cross product between a vector and the Z component of a vector
 * @method crossZV
 * @static
 * @param  {Array} out
 * @param  {Number} zcomp
 * @param  {Array} vec
 * @return {Array}
 */
vec2.crossZV = function(out, zcomp, vec){
    vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule
    vec2.scale(out,out,zcomp);      // Scale with z
    return out;
};

/**
 * Rotate a vector by an angle
 * @method rotate
 * @static
 * @param  {Array} out
 * @param  {Array} a
 * @param  {Number} angle
 * @return {Array}
 */
vec2.rotate = function(out,a,angle){
    if(angle !== 0){
        var c = Math.cos(angle),
            s = Math.sin(angle),
            x = a[0],
            y = a[1];
        out[0] = c*x -s*y;
        out[1] = s*x +c*y;
    } else {
        out[0] = a[0];
        out[1] = a[1];
    }
    return out;
};

/**
 * Rotate a vector 90 degrees clockwise
 * @method rotate90cw
 * @static
 * @param  {Array} out
 * @param  {Array} a
 * @param  {Number} angle
 * @return {Array}
 */
vec2.rotate90cw = function(out, a) {
    var x = a[0];
    var y = a[1];
    out[0] = y;
    out[1] = -x;
    return out;
};

/**
 * Transform a point position to local frame.
 * @method toLocalFrame
 * @param  {Array} out
 * @param  {Array} worldPoint
 * @param  {Array} framePosition
 * @param  {Number} frameAngle
 * @return {Array}
 */
vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){
    var c = Math.cos(-frameAngle),
        s = Math.sin(-frameAngle),
        x = worldPoint[0] - framePosition[0],
        y = worldPoint[1] - framePosition[1];
    out[0] = c * x - s * y;
    out[1] = s * x + c * y;
    return out;
};

/**
 * Transform a point position to global frame.
 * @method toGlobalFrame
 * @param  {Array} out
 * @param  {Array} localPoint
 * @param  {Array} framePosition
 * @param  {Number} frameAngle
 */
vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){
    var c = Math.cos(frameAngle),
        s = Math.sin(frameAngle),
        x = localPoint[0],
        y = localPoint[1],
        addX = framePosition[0],
        addY = framePosition[1];
    out[0] = c * x - s * y + addX;
    out[1] = s * x + c * y + addY;
};

/**
 * Transform a vector to local frame.
 * @method vectorToLocalFrame
 * @param  {Array} out
 * @param  {Array} worldVector
 * @param  {Number} frameAngle
 * @return {Array}
 */
vec2.vectorToLocalFrame = function(out, worldVector, frameAngle){
    var c = Math.cos(-frameAngle),
        s = Math.sin(-frameAngle),
        x = worldVector[0],
        y = worldVector[1];
    out[0] = c*x -s*y;
    out[1] = s*x +c*y;
    return out;
};

/**
 * Transform a vector to global frame.
 * @method vectorToGlobalFrame
 * @param  {Array} out
 * @param  {Array} localVector
 * @param  {Number} frameAngle
 */
vec2.vectorToGlobalFrame = vec2.rotate;

/**
 * Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php
 * @method centroid
 * @static
 * @param  {Array} out
 * @param  {Array} a
 * @param  {Array} b
 * @param  {Array} c
 * @return  {Array} The "out" vector.
 */
vec2.centroid = function(out, a, b, c){
    vec2.add(out, a, b);
    vec2.add(out, out, c);
    vec2.scale(out, out, 1/3);
    return out;
};

/**
 * Creates a new, empty vec2
 * @static
 * @method create
 * @return {Array} a new 2D vector
 */
vec2.create = function() {
    var out = new Utils.ARRAY_TYPE(2);
    out[0] = 0;
    out[1] = 0;
    return out;
};

/**
 * Creates a new vec2 initialized with values from an existing vector
 * @static
 * @method clone
 * @param {Array} a vector to clone
 * @return {Array} a new 2D vector
 */
vec2.clone = function(a) {
    var out = new Utils.ARRAY_TYPE(2);
    out[0] = a[0];
    out[1] = a[1];
    return out;
};

/**
 * Creates a new vec2 initialized with the given values
 * @static
 * @method fromValues
 * @param {Number} x X component
 * @param {Number} y Y component
 * @return {Array} a new 2D vector
 */
vec2.fromValues = function(x, y) {
    var out = new Utils.ARRAY_TYPE(2);
    out[0] = x;
    out[1] = y;
    return out;
};

/**
 * Copy the values from one vec2 to another
 * @static
 * @method copy
 * @param {Array} out the receiving vector
 * @param {Array} a the source vector
 * @return {Array} out
 */
vec2.copy = function(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    return out;
};

/**
 * Set the components of a vec2 to the given values
 * @static
 * @method set
 * @param {Array} out the receiving vector
 * @param {Number} x X component
 * @param {Number} y Y component
 * @return {Array} out
 */
vec2.set = function(out, x, y) {
    out[0] = x;
    out[1] = y;
    return out;
};

/**
 * Adds two vec2's
 * @static
 * @method add
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.add = function(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    return out;
};

/**
 * Subtracts two vec2's
 * @static
 * @method subtract
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.subtract = function(out, a, b) {
    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];
    return out;
};

/**
 * Multiplies two vec2's
 * @static
 * @method multiply
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.multiply = function(out, a, b) {
    out[0] = a[0] * b[0];
    out[1] = a[1] * b[1];
    return out;
};

/**
 * Divides two vec2's
 * @static
 * @method divide
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.divide = function(out, a, b) {
    out[0] = a[0] / b[0];
    out[1] = a[1] / b[1];
    return out;
};

/**
 * Scales a vec2 by a scalar number
 * @static
 * @method scale
 * @param {Array} out the receiving vector
 * @param {Array} a the vector to scale
 * @param {Number} b amount to scale the vector by
 * @return {Array} out
 */
vec2.scale = function(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1] * b;
    return out;
};

/**
 * Calculates the euclidian distance between two vec2's
 * @static
 * @method distance
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Number} distance between a and b
 */
vec2.distance = function(a, b) {
    var x = b[0] - a[0],
        y = b[1] - a[1];
    return Math.sqrt(x*x + y*y);
};

/**
 * Calculates the squared euclidian distance between two vec2's
 * @static
 * @method squaredDistance
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Number} squared distance between a and b
 */
vec2.squaredDistance = function(a, b) {
    var x = b[0] - a[0],
        y = b[1] - a[1];
    return x*x + y*y;
};

/**
 * Calculates the length of a vec2
 * @static
 * @method length
 * @param {Array} a vector to calculate length of
 * @return {Number} length of a
 */
vec2.length = function (a) {
    var x = a[0],
        y = a[1];
    return Math.sqrt(x*x + y*y);
};

/**
 * Calculates the squared length of a vec2
 * @static
 * @method squaredLength
 * @param {Array} a vector to calculate squared length of
 * @return {Number} squared length of a
 */
vec2.squaredLength = function (a) {
    var x = a[0],
        y = a[1];
    return x*x + y*y;
};

/**
 * Negates the components of a vec2
 * @static
 * @method negate
 * @param {Array} out the receiving vector
 * @param {Array} a vector to negate
 * @return {Array} out
 */
vec2.negate = function(out, a) {
    out[0] = -a[0];
    out[1] = -a[1];
    return out;
};

/**
 * Normalize a vec2
 * @static
 * @method normalize
 * @param {Array} out the receiving vector
 * @param {Array} a vector to normalize
 * @return {Array} out
 */
vec2.normalize = function(out, a) {
    var x = a[0],
        y = a[1];
    var len = x*x + y*y;
    if (len > 0) {
        //TODO: evaluate use of glm_invsqrt here?
        len = 1 / Math.sqrt(len);
        out[0] = a[0] * len;
        out[1] = a[1] * len;
    }
    return out;
};

/**
 * Calculates the dot product of two vec2's
 * @static
 * @method dot
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Number} dot product of a and b
 */
vec2.dot = function (a, b) {
    return a[0] * b[0] + a[1] * b[1];
};

/**
 * Returns a string representation of a vector
 * @static
 * @method str
 * @param {Array} vec vector to represent as a string
 * @return {String} string representation of the vector
 */
vec2.str = function (a) {
    return 'vec2(' + a[0] + ', ' + a[1] + ')';
};

/**
 * Linearly interpolate/mix two vectors.
 * @static
 * @method lerp
 * @param {Array} out
 * @param {Array} a First vector
 * @param {Array} b Second vector
 * @param {number} t Lerp factor
 */
vec2.lerp = function (out, a, b, t) {
    var ax = a[0],
        ay = a[1];
    out[0] = ax + t * (b[0] - ax);
    out[1] = ay + t * (b[1] - ay);
    return out;
};

/**
 * Reflect a vector along a normal.
 * @static
 * @method reflect
 * @param {Array} out
 * @param {Array} vector
 * @param {Array} normal
 */
vec2.reflect = function(out, vector, normal){
    var dot = vector[0] * normal[0] + vector[1] * normal[1];
    out[0] = vector[0] - 2 * normal[0] * dot;
    out[1] = vector[1] - 2 * normal[1] * dot;
};

/**
 * Get the intersection point between two line segments.
 * @static
 * @method getLineSegmentsIntersection
 * @param  {Array} out
 * @param  {Array} p0
 * @param  {Array} p1
 * @param  {Array} p2
 * @param  {Array} p3
 * @return {boolean} True if there was an intersection, otherwise false.
 */
vec2.getLineSegmentsIntersection = function(out, p0, p1, p2, p3) {
    var t = vec2.getLineSegmentsIntersectionFraction(p0, p1, p2, p3);
    if(t < 0){
        return false;
    } else {
        out[0] = p0[0] + (t * (p1[0] - p0[0]));
        out[1] = p0[1] + (t * (p1[1] - p0[1]));
        return true;
    }
};

/**
 * Get the intersection fraction between two line segments. If successful, the intersection is at p0 + t * (p1 - p0)
 * @static
 * @method getLineSegmentsIntersectionFraction
 * @param  {Array} p0
 * @param  {Array} p1
 * @param  {Array} p2
 * @param  {Array} p3
 * @return {number} A number between 0 and 1 if there was an intersection, otherwise -1.
 */
vec2.getLineSegmentsIntersectionFraction = function(p0, p1, p2, p3) {
    var s1_x = p1[0] - p0[0];
    var s1_y = p1[1] - p0[1];
    var s2_x = p3[0] - p2[0];
    var s2_y = p3[1] - p2[1];

    var s, t;
    s = (-s1_y * (p0[0] - p2[0]) + s1_x * (p0[1] - p2[1])) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0[1] - p2[1]) - s2_y * (p0[0] - p2[0])) / (-s2_x * s1_y + s1_x * s2_y);
    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { // Collision detected
        return t;
    }
    return -1; // No collision
};


./pixelrender/BaseRenderer.js
import Pool from "./Pool";

export default class BaseRenderer {
  constructor(element, stroke) {
    this.pool = new Pool();
    this.element = element;
    this.stroke = stroke;
    this.circleConf = { isCircle: true };

    this.initEventHandler();
    this.name = "BaseRenderer";
  }

  setStroke(color = "#000000", thinkness = 1) {
    this.stroke = { color, thinkness };
  }

  initEventHandler() {
    this._protonUpdateHandler = () => {
      this.onProtonUpdate.call(this);
    };

    this._protonUpdateAfterHandler = () => {
      this.onProtonUpdateAfter.call(this);
    };

    this._emitterAddedHandler = emitter => {
      this.onEmitterAdded.call(this, emitter);
    };

    this._emitterRemovedHandler = emitter => {
      this.onEmitterRemoved.call(this, emitter);
    };

    this._particleCreatedHandler = particle => {
      this.onParticleCreated.call(this, particle);
    };

    this._particleUpdateHandler = particle => {
      this.onParticleUpdate.call(this, particle);
    };

    this._particleDeadHandler = particle => {
      this.onParticleDead.call(this, particle);
    };
  }

  init(proton) {
    this.parent = proton;

    proton.addEventListener("PROTON_UPDATE", this._protonUpdateHandler);
    proton.addEventListener("PROTON_UPDATE_AFTER", this._protonUpdateAfterHandler);

    proton.addEventListener("EMITTER_ADDED", this._emitterAddedHandler);
    proton.addEventListener("EMITTER_REMOVED", this._emitterRemovedHandler);

    proton.addEventListener("PARTICLE_CREATED", this._particleCreatedHandler);
    proton.addEventListener("PARTICLE_UPDATE", this._particleUpdateHandler);
    proton.addEventListener("PARTICLE_DEAD", this._particleDeadHandler);
  }

  resize(width, height) {}

  destroy() {
    this.remove();
    this.pool.destroy();
    this.pool = null;
    this.element = null;
    this.stroke = null;
  }

  remove(proton) {
    this.parent.removeEventListener("PROTON_UPDATE", this._protonUpdateHandler);
    this.parent.removeEventListener("PROTON_UPDATE_AFTER", this._protonUpdateAfterHandler);

    this.parent.removeEventListener("EMITTER_ADDED", this._emitterAddedHandler);
    this.parent.removeEventListener("EMITTER_REMOVED", this._emitterRemovedHandler);

    this.parent.removeEventListener("PARTICLE_CREATED", this._particleCreatedHandler);
    this.parent.removeEventListener("PARTICLE_UPDATE", this._particleUpdateHandler);
    this.parent.removeEventListener("PARTICLE_DEAD", this._particleDeadHandler);

    this.parent = null;
  }

  onProtonUpdate() {}
  onProtonUpdateAfter() {}

  onEmitterAdded(emitter) {}
  onEmitterRemoved(emitter) {}

  onParticleCreated(particle) {}
  onParticleUpdate(particle) {}
  onParticleDead(particle) {}
}

./pixelrender/DomUtil.js
export default {
  /**
   * Creates and returns a new canvas. The opacity is by default set to 0
   *
   * @memberof Proton#Proton.DomUtil
   * @method createCanvas
   *
   * @param {String} $id the canvas' id
   * @param {Number} $width the canvas' width
   * @param {Number} $height the canvas' height
   * @param {String} [$position=absolute] the canvas' position, default is 'absolute'
   *
   * @return {Object}
   */
  createCanvas(id, width, height, position = "absolute") {
    const dom = document.createElement("canvas");

    dom.id = id;
    dom.width = width;
    dom.height = height;
    dom.style.opacity = 0;
    dom.style.position = position;
    this.transform(dom, -500, -500, 0, 0);

    return dom;
  },

  createDiv(id, width, height) {
    const dom = document.createElement("div");

    dom.id = id;
    dom.style.position = "absolute";
    this.resize(dom, width, height);

    return dom;
  },

  resize(dom, width, height) {
    dom.style.width = width + "px";
    dom.style.height = height + "px";
    dom.style.marginLeft = -width / 2 + "px";
    dom.style.marginTop = -height / 2 + "px";
  },

  /**
   * Adds a transform: translate(), scale(), rotate() to a given div dom for all browsers
   *
   * @memberof Proton#Proton.DomUtil
   * @method transform
   *
   * @param {HTMLDivElement} div
   * @param {Number} $x
   * @param {Number} $y
   * @param {Number} $scale
   * @param {Number} $rotate
   */
  transform(div, x, y, scale, rotate) {
    div.style.willChange = "transform";
    const transform = `translate(${x}px, ${y}px) scale(${scale}) rotate(${rotate}deg)`;
    this.css3(div, "transform", transform);
  },

  transform3d(div, x, y, scale, rotate) {
    div.style.willChange = "transform";
    const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale}) rotate(${rotate}deg)`;
    this.css3(div, "backfaceVisibility", "hidden");
    this.css3(div, "transform", transform);
  },

  css3(div, key, val) {
    const bkey = key.charAt(0).toUpperCase() + key.substr(1);

    div.style[`Webkit${bkey}`] = val;
    div.style[`Moz${bkey}`] = val;
    div.style[`O${bkey}`] = val;
    div.style[`ms${bkey}`] = val;
    div.style[`${key}`] = val;
  }
};

./pixelrender/ImgUtil.js
import WebGLUtil from "./WebGLUtil";
import DomUtil from "./DomUtil";

const imgsCache = {};
const canvasCache = {};
let canvasId = 0;

export default {
  /**
   * This will get the image data. It could be necessary to create a Proton.Zone.
   *
   * @memberof Proton#Proton.Util
   * @method getImageData
   *
   * @param {HTMLCanvasElement}   context any canvas, must be a 2dContext 'canvas.getContext('2d')'
   * @param {Object}              image   could be any dom image, e.g. document.getElementById('thisIsAnImgTag');
   * @param {Proton.Rectangle}    rect
   */
  getImageData(context, image, rect) {
    context.drawImage(image, rect.x, rect.y);
    const imagedata = context.getImageData(rect.x, rect.y, rect.width, rect.height);
    context.clearRect(rect.x, rect.y, rect.width, rect.height);

    return imagedata;
  },

  /**
   * @memberof Proton#Proton.Util
   * @method getImgFromCache
   *
   * @todo add description
   * @todo describe func
   *
   * @param {Mixed}               img
   * @param {Proton.Particle}     particle
   * @param {Boolean}             drawCanvas  set to true if a canvas should be saved into particle.data.canvas
   * @param {Boolean}             func
   */
  getImgFromCache(img, callback, param) {
    const src = typeof img === "string" ? img : img.src;

    if (imgsCache[src]) {
      callback(imgsCache[src], param);
    } else {
      const image = new Image();
      image.onload = e => {
        imgsCache[src] = e.target;
        callback(imgsCache[src], param);
      };

      image.src = src;
    }
  },

  getCanvasFromCache(img, callback, param) {
    const src = img.src;

    if (!canvasCache[src]) {
      const width = WebGLUtil.nhpot(img.width);
      const height = WebGLUtil.nhpot(img.height);

      const canvas = DomUtil.createCanvas(`proton_canvas_cache_${++canvasId}`, width, height);
      const context = canvas.getContext("2d");
      context.drawImage(img, 0, 0, img.width, img.height);

      canvasCache[src] = canvas;
    }

    callback && callback(canvasCache[src], param);

    return canvasCache[src];
  }
};

./pixelrender/PixelRenderer.js
import Rectangle from "./Rectangle";
import BaseRenderer from "./BaseRenderer";

export default class PixelRenderer extends BaseRenderer {
  constructor(element, rectangle) {
    super(element);

    this.context = this.element.getContext("2d");
    this.imageData = null;
    this.rectangle = rectangle;
    this.createImageData(rectangle);

    this.name = "PixelRenderer";
  }

  resize(width, height) {
    this.element.width = width;
    this.element.height = height;
  }

  createImageData(rectangle) {
    this.rectangle = rectangle ? rectangle : new Rectangle(0, 0, this.element.width, this.element.height);
    this.imageData = this.context.createImageData(this.rectangle.width, this.rectangle.height);
    this.context.putImageData(this.imageData, this.rectangle.x, this.rectangle.y);
  }

  onProtonUpdate() {
    this.context.clearRect(this.rectangle.x, this.rectangle.y, this.rectangle.width, this.rectangle.height);
    this.imageData = this.context.getImageData(
      this.rectangle.x,
      this.rectangle.y,
      this.rectangle.width,
      this.rectangle.height
    );
  }

  onProtonUpdateAfter() {
    this.context.putImageData(this.imageData, this.rectangle.x, this.rectangle.y);
  }

  onParticleCreated(particle) {}

  onParticleUpdate(particle) {
    if (this.imageData) {
      this.setPixel(
        this.imageData,
        (particle.p.x - this.rectangle.x) >> 0,
        (particle.p.y - this.rectangle.y) >> 0,
        particle
      );
    }
  }

  setPixel(imagedata, x, y, particle) {
    const rgb = particle.rgb;
    if (x < 0 || x > this.element.width || y < 0 || y > this.element.height) return;

    const i = ((y >> 0) * imagedata.width + (x >> 0)) * 4;
    imagedata.data[i] = rgb.r;
    imagedata.data[i + 1] = rgb.g;
    imagedata.data[i + 2] = rgb.b;
    imagedata.data[i + 3] = particle.alpha * 255;
  }

  onParticleDead(particle) {}

  destroy() {
    super.destroy();
    this.stroke = null;
    this.context = null;
    this.imageData = null;
    this.rectangle = null;
  }
}

./pixelrender/Pool.js
/**
 * Pool is the cache pool of the proton engine, it is very important.
 *
 * get(target, params, uid)
 *  Class
 *    uid = Puid.getId -> Puid save target cache
 *    target.__puid = uid
 *
 *  body
 *    uid = Puid.getId -> Puid save target cache
 *
 *
 * expire(target)
 *  cache[target.__puid] push target
 *
 */
import Util from "./Util";
import Puid from "./Puid";

export default class Pool {
  /**
   * @memberof! Proton#
   * @constructor
   * @alias Proton.Pool
   *
   * @todo add description
   * @todo add description of properties
   *
   * @property {Number} total
   * @property {Object} cache
   */
  constructor(num) {
    this.total = 0;
    this.cache = {};
  }

  /**
   * @todo add description
   *
   * @method get
   * @memberof Proton#Proton.Pool
   *
   * @param {Object|Function} target
   * @param {Object} [params] just add if `target` is a function
   *
   * @return {Object}
   */
  get(target, params, uid) {
    let p;
    uid = uid || target.__puid || Puid.getId(target);

    if (this.cache[uid] && this.cache[uid].length > 0) {
      p = this.cache[uid].pop();
    } else {
      p = this.createOrClone(target, params);
    }

    p.__puid = target.__puid || uid;
    return p;
  }

  /**
   * @todo add description
   *
   * @method set
   * @memberof Proton#Proton.Pool
   *
   * @param {Object} target
   *
   * @return {Object}
   */
  expire(target) {
    return this.getCache(target.__puid).push(target);
  }

  /**
   * Creates a new class instance
   *
   * @todo add more documentation
   *
   * @method create
   * @memberof Proton#Proton.Pool
   *
   * @param {Object|Function} target any Object or Function
   * @param {Object} [params] just add if `target` is a function
   *
   * @return {Object}
   */
  createOrClone(target, params) {
    this.total++;

    if (this.create) {
      return this.create(target, params);
    } else if (typeof target === "function") {
      return Util.classApply(target, params);
    } else {
      return target.clone();
    }
  }

  /**
   * @todo add description - what is in the cache?
   *
   * @method getCount
   * @memberof Proton#Proton.Pool
   *
   * @return {Number}
   */
  getCount() {
    let count = 0;
    for (let id in this.cache) count += this.cache[id].length;
    return count++;
  }

  /**
   * Destroyes all items from Pool.cache
   *
   * @method destroy
   * @memberof Proton#Proton.Pool
   */
  destroy() {
    for (let id in this.cache) {
      this.cache[id].length = 0;
      delete this.cache[id];
    }
  }

  /**
   * Returns Pool.cache
   *
   * @method getCache
   * @memberof Proton#Proton.Pool
   * @private
   *
   * @param {Number} uid the unique id
   *
   * @return {Object}
   */
  getCache(uid = "default") {
    if (!this.cache[uid]) this.cache[uid] = [];
    return this.cache[uid];
  }
}

./pixelrender/Puid.js
const idsMap = {};

const Puid = {
  _index: 0,
  _cache: {},

  id(type) {
    if (idsMap[type] === undefined || idsMap[type] === null) idsMap[type] = 0;
    return `${type}_${idsMap[type]++}`;
  },

  getId(target) {
    let uid = this.getIdFromCache(target);
    if (uid) return uid;

    uid = `PUID_${this._index++}`;
    this._cache[uid] = target;
    return uid;
  },

  getIdFromCache(target) {
    let obj, id;

    for (id in this._cache) {
      obj = this._cache[id];

      if (obj === target) return id;
      if (this.isBody(obj, target) && obj.src === target.src) return id;
    }

    return null;
  },

  isBody(obj, target) {
    return typeof obj === "object" && typeof target === "object" && obj.isInner && target.isInner;
  },

  getTarget(uid) {
    return this._cache[uid];
  }
};

export default Puid;

./pixelrender/Rectangle.js
export default class Rectangle {
  constructor(x, y, w, h) {
    this.x = x;
    this.y = y;

    this.width = w;
    this.height = h;

    this.bottom = this.y + this.height;
    this.right = this.x + this.width;
  }

  contains(x, y) {
    if (x <= this.right && x >= this.x && y <= this.bottom && y >= this.y) return true;
    else return false;
  }
}

./pixelrender/Util.js
import ImgUtil from "./ImgUtil";

export default {
  /**
   * Returns the default if the value is null or undefined
   *
   * @memberof Proton#Proton.Util
   * @method initValue
   *
   * @param {Mixed} value a specific value, could be everything but null or undefined
   * @param {Mixed} defaults the default if the value is null or undefined
   */
  initValue(value, defaults) {
    value = value !== null && value !== undefined ? value : defaults;
    return value;
  },

  /**
   * Checks if the value is a valid array
   *
   * @memberof Proton#Proton.Util
   * @method isArray
   *
   * @param {Array} value Any array
   *
   * @returns {Boolean}
   */
  isArray(value) {
    return Object.prototype.toString.call(value) === "[object Array]";
  },

  /**
   * Destroyes the given array
   *
   * @memberof Proton#Proton.Util
   * @method emptyArray
   *
   * @param {Array} array Any array
   */
  emptyArray(arr) {
    if (arr) arr.length = 0;
  },

  toArray(arr) {
    return this.isArray(arr) ? arr : [arr];
  },

  sliceArray(arr1, index, arr2) {
    this.emptyArray(arr2);
    for (let i = index; i < arr1.length; i++) {
      arr2.push(arr1[i]);
    }
  },

  getRandFromArray(arr) {
    if (!arr) return null;
    return arr[Math.floor(arr.length * Math.random())];
  },

  /**
   * Destroyes the given object
   *
   * @memberof Proton#Proton.Util
   * @method emptyObject
   *
   * @param {Object} obj Any object
   */
  emptyObject(obj, ignore = null) {
    for (let key in obj) {
      if (ignore && ignore.indexOf(key) > -1) continue;
      delete obj[key];
    }
  },

  /**
   * Makes an instance of a class and binds the given array
   *
   * @memberof Proton#Proton.Util
   * @method classApply
   *
   * @param {Function} constructor A class to make an instance from
   * @param {Array} [args] Any array to bind it to the constructor
   *
   * @return {Object} The instance of constructor, optionally bind with args
   */
  classApply(constructor, args = null) {
    if (!args) {
      return new constructor();
    } else {
      const FactoryFunc = constructor.bind.apply(constructor, [null].concat(args));
      return new FactoryFunc();
    }
  },

  /**
   * This will get the image data. It could be necessary to create a Proton.Zone.
   *
   * @memberof Proton#Proton.Util
   * @method getImageData
   *
   * @param {HTMLCanvasElement}   context any canvas, must be a 2dContext 'canvas.getContext('2d')'
   * @param {Object}              image   could be any dom image, e.g. document.getElementById('thisIsAnImgTag');
   * @param {Proton.Rectangle}    rect
   */
  getImageData(context, image, rect) {
    return ImgUtil.getImageData(context, image, rect);
  },

  destroyAll(arr, param = null) {
    let i = arr.length;

    while (i--) {
      try {
        arr[i].destroy(param);
      } catch (e) {}

      delete arr[i];
    }

    arr.length = 0;
  },

  assign(target, source) {
    if (typeof Object.assign !== "function") {
      for (let key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      }

      return target;
    } else {
      return Object.assign(target, source);
    }
  }
};

./pixelrender/WebGLUtil.js
export default {
  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method ipot
   *
   * @todo add description
   * @todo add length description
   *
   * @param {Number} length
   *
   * @return {Boolean}
   */
  ipot(length) {
    return (length & (length - 1)) === 0;
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method nhpot
   *
   * @todo add description
   * @todo add length description
   *
   * @param {Number} length
   *
   * @return {Number}
   */
  nhpot(length) {
    --length;
    for (let i = 1; i < 32; i <<= 1) {
      length = length | (length >> i);
    }

    return length + 1;
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method makeTranslation
   *
   * @todo add description
   * @todo add tx, ty description
   * @todo add return description
   *
   * @param {Number} tx either 0 or 1
   * @param {Number} ty either 0 or 1
   *
   * @return {Object}
   */
  makeTranslation(tx, ty) {
    return [1, 0, 0, 0, 1, 0, tx, ty, 1];
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method makeRotation
   *
   * @todo add description
   * @todo add return description
   *
   * @param {Number} angleInRadians
   *
   * @return {Object}
   */
  makeRotation(angleInRadians) {
    let c = Math.cos(angleInRadians);
    let s = Math.sin(angleInRadians);

    return [c, -s, 0, s, c, 0, 0, 0, 1];
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method makeScale
   *
   * @todo add description
   * @todo add tx, ty description
   * @todo add return description
   *
   * @param {Number} sx either 0 or 1
   * @param {Number} sy either 0 or 1
   *
   * @return {Object}
   */
  makeScale(sx, sy) {
    return [sx, 0, 0, 0, sy, 0, 0, 0, 1];
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method matrixMultiply
   *
   * @todo add description
   * @todo add a, b description
   * @todo add return description
   *
   * @param {Object} a
   * @param {Object} b
   *
   * @return {Object}
   */
  matrixMultiply(a, b) {
    let a00 = a[0 * 3 + 0];
    let a01 = a[0 * 3 + 1];
    let a02 = a[0 * 3 + 2];
    let a10 = a[1 * 3 + 0];
    let a11 = a[1 * 3 + 1];
    let a12 = a[1 * 3 + 2];
    let a20 = a[2 * 3 + 0];
    let a21 = a[2 * 3 + 1];
    let a22 = a[2 * 3 + 2];
    let b00 = b[0 * 3 + 0];
    let b01 = b[0 * 3 + 1];
    let b02 = b[0 * 3 + 2];
    let b10 = b[1 * 3 + 0];
    let b11 = b[1 * 3 + 1];
    let b12 = b[1 * 3 + 2];
    let b20 = b[2 * 3 + 0];
    let b21 = b[2 * 3 + 1];
    let b22 = b[2 * 3 + 2];

    return [
      a00 * b00 + a01 * b10 + a02 * b20,
      a00 * b01 + a01 * b11 + a02 * b21,
      a00 * b02 + a01 * b12 + a02 * b22,
      a10 * b00 + a11 * b10 + a12 * b20,
      a10 * b01 + a11 * b11 + a12 * b21,
      a10 * b02 + a11 * b12 + a12 * b22,
      a20 * b00 + a21 * b10 + a22 * b20,
      a20 * b01 + a21 * b11 + a22 * b21,
      a20 * b02 + a21 * b12 + a22 * b22
    ];
  }
};


./plane/Plane.js
var Shape =  require('./Shape')
,    vec2 =  require('./vec2')
,    Utils = require('./Utils');

module.exports = Plane;

/**
 * Plane shape class. The plane is facing in the Y direction.
 * @class Plane
 * @extends Shape
 * @constructor
 * @param {object} [options] (Note that this options object will be passed on to the {{#crossLink "Shape"}}{{/crossLink}} constructor.)
 * @example
 *     var body = new Body();
 *     var shape = new Plane();
 *     body.addShape(shape);
 */
function Plane(options){
    options = options ? Utils.shallowClone(options) : {};
    options.type = Shape.PLANE;
    Shape.call(this, options);
}
Plane.prototype = new Shape();
Plane.prototype.constructor = Plane;

/**
 * Compute moment of inertia
 * @method computeMomentOfInertia
 */
Plane.prototype.computeMomentOfInertia = function(){
    return 0; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
};

/**
 * Update the bounding radius
 * @method updateBoundingRadius
 */
Plane.prototype.updateBoundingRadius = function(){
    this.boundingRadius = Number.MAX_VALUE;
};

/**
 * @method computeAABB
 * @param  {AABB}   out
 * @param  {Array}  position
 * @param  {Number} angle
 */
Plane.prototype.computeAABB = function(out, position, angle){
    var a = angle % (2 * Math.PI);
    var set = vec2.set;
    var max = 1e7;
    var lowerBound = out.lowerBound;
    var upperBound = out.upperBound;

    // Set max bounds
    set(lowerBound, -max, -max);
    set(upperBound,  max,  max);

    if(a === 0){
        // y goes from -inf to 0
        upperBound[1] = position[1];

    } else if(a === Math.PI / 2){

        // x goes from 0 to inf
        lowerBound[0] = position[0];

    } else if(a === Math.PI){

        // y goes from 0 to inf
        lowerBound[1] = position[1];

    } else if(a === 3*Math.PI/2){

        // x goes from -inf to 0
        upperBound[0] = position[0];

    }
};

Plane.prototype.updateArea = function(){
    this.area = Number.MAX_VALUE;
};

var intersectPlane_planePointToFrom = vec2.create();
var intersectPlane_normal = vec2.create();
var intersectPlane_len = vec2.create();

/**
 * @method raycast
 * @param  {RayResult} result
 * @param  {Ray} ray
 * @param  {array} position
 * @param  {number} angle
 */
Plane.prototype.raycast = function(result, ray, position, angle){
    var from = ray.from;
    var to = ray.to;
    var direction = ray.direction;
    var planePointToFrom = intersectPlane_planePointToFrom;
    var normal = intersectPlane_normal;
    var len = intersectPlane_len;

    // Get plane normal
    vec2.set(normal, 0, 1);
    vec2.rotate(normal, normal, angle);

    vec2.subtract(len, from, position);
    var planeToFrom = vec2.dot(len, normal);
    vec2.subtract(len, to, position);
    var planeToTo = vec2.dot(len, normal);

    if(planeToFrom * planeToTo > 0){
        // "from" and "to" are on the same side of the plane... bail out
        return;
    }

    if(vec2.squaredDistance(from, to) < planeToFrom * planeToFrom){
        return;
    }

    var n_dot_dir = vec2.dot(normal, direction);

    vec2.subtract(planePointToFrom, from, position);
    var t = -vec2.dot(normal, planePointToFrom) / n_dot_dir / ray.length;

    ray.reportIntersection(result, t, normal, -1);
};

Plane.prototype.pointTest = function(localPoint){
    return localPoint[1] <= 0;
};

./plane/Shape.js
module.exports = Shape;

var vec2 = require('./vec2');

/**
 * Base class for shapes. Not to be used directly.
 * @class Shape
 * @constructor
 * @param {object} [options]
 * @param {number} [options.angle=0]
 * @param {number} [options.collisionGroup=1]
 * @param {number} [options.collisionMask=1]
 * @param {boolean} [options.collisionResponse=true]
 * @param {Material} [options.material=null]
 * @param {array} [options.position]
 * @param {boolean} [options.sensor=false]
 * @param {object} [options.type=0]
 */
function Shape(options){
    options = options || {};

    /**
     * The body this shape is attached to. A shape can only be attached to a single body.
     * @property {Body} body
     */
    this.body = null;

    /**
     * Body-local position of the shape.
     * @property {Array} position
     */
    this.position = vec2.create();
    if(options.position){
        vec2.copy(this.position, options.position);
    }

    /**
     * Body-local angle of the shape.
     * @property {number} angle
     */
    this.angle = options.angle || 0;

    /**
     * The type of the shape. One of:
     *
     *

     *
Shape.CIRCLE

     *
Shape.PARTICLE

     *
Shape.PLANE

     *
Shape.CONVEX

     *
Shape.LINE

     *
Shape.BOX

     *
Shape.CAPSULE

     *
Shape.HEIGHTFIELD

     *

     *
     * @property {number} type
     */
    this.type = options.type || 0;

    /**
     * Shape object identifier. Read only.
     * @readonly
     * @type {Number}
     * @property id
     */
    this.id = Shape.idCounter++;

    /**
     * Bounding circle radius of this shape
     * @readonly
     * @property boundingRadius
     * @type {Number}
     */
    this.boundingRadius = 0;

    /**
     * Collision group that this shape belongs to (bit mask). See this tutorial.
     * @property collisionGroup
     * @type {Number}
     * @example
     *     // Setup bits for each available group
     *     var PLAYER = Math.pow(2,0),
     *         ENEMY =  Math.pow(2,1),
     *         GROUND = Math.pow(2,2)
     *
     *     // Put shapes into their groups
     *     player1Shape.collisionGroup = PLAYER;
     *     player2Shape.collisionGroup = PLAYER;
     *     enemyShape  .collisionGroup = ENEMY;
     *     groundShape .collisionGroup = GROUND;
     *
     *     // Assign groups that each shape collide with.
     *     // Note that the players can collide with ground and enemies, but not with other players.
     *     player1Shape.collisionMask = ENEMY | GROUND;
     *     player2Shape.collisionMask = ENEMY | GROUND;
     *     enemyShape  .collisionMask = PLAYER | GROUND;
     *     groundShape .collisionMask = PLAYER | ENEMY;
     *
     * @example
     *     // How collision check is done
     *     if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
     *         // The shapes will collide
     *     }
     */
    this.collisionGroup = options.collisionGroup !== undefined ? options.collisionGroup : 1;

    /**
     * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. That means that this shape will move through other body shapes, but it will still trigger contact events, etc.
     * @property {Boolean} collisionResponse
     */
    this.collisionResponse = options.collisionResponse !== undefined ? options.collisionResponse : true;

    /**
     * Collision mask of this shape. See .collisionGroup.
     * @property collisionMask
     * @type {Number}
     */
    this.collisionMask = options.collisionMask !== undefined ? options.collisionMask : 1;

    /**
     * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead.
     * @property material
     * @type {Material}
     */
    this.material = options.material || null;

    /**
     * Area of this shape.
     * @property area
     * @type {Number}
     */
    this.area = 0;

    /**
     * Set to true if you want this shape to be a sensor. A sensor does not generate contacts, but it still reports contact events. This is good if you want to know if a shape is overlapping another shape, without them generating contacts.
     * @property {Boolean} sensor
     */
    this.sensor = options.sensor !== undefined ? options.sensor : false;

    if(this.type){
        this.updateBoundingRadius();
    }

    this.updateArea();
}

Shape.idCounter = 0;

/**
 * @static
 * @property {Number} CIRCLE
 */
Shape.CIRCLE =      1;

/**
 * @static
 * @property {Number} PARTICLE
 */
Shape.PARTICLE =    2;

/**
 * @static
 * @property {Number} PLANE
 */
Shape.PLANE =       4;

/**
 * @static
 * @property {Number} CONVEX
 */
Shape.CONVEX =      8;

/**
 * @static
 * @property {Number} LINE
 */
Shape.LINE =        16;

/**
 * @static
 * @property {Number} BOX
 */
Shape.BOX =   32;

/**
 * @static
 * @property {Number} CAPSULE
 */
Shape.CAPSULE = 64;

/**
 * @static
 * @property {Number} HEIGHTFIELD
 */
Shape.HEIGHTFIELD = 128;

Shape.prototype = {

    /**
     * Should return the moment of inertia around the Z axis of the body. See Wikipedia's list of moments of inertia.
     * @method computeMomentOfInertia
     * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0.
     */
    computeMomentOfInertia: function(){},

    /**
     * Returns the bounding circle radius of this shape.
     * @method updateBoundingRadius
     * @return {Number}
     */
    updateBoundingRadius: function(){},

    /**
     * Update the .area property of the shape.
     * @method updateArea
     */
    updateArea: function(){},

    /**
     * Compute the world axis-aligned bounding box (AABB) of this shape.
     * @method computeAABB
     * @param  {AABB} out The resulting AABB.
     * @param  {Array} position World position of the shape.
     * @param  {Number} angle World angle of the shape.
     */
    computeAABB: function(/*out, position, angle*/){
        // To be implemented in each subclass
    },

    /**
     * Perform raycasting on this shape.
     * @method raycast
     * @param  {RayResult} result Where to store the resulting data.
     * @param  {Ray} ray The Ray that you want to use for raycasting.
     * @param  {array} position World position of the shape (the .position property will be ignored).
     * @param  {number} angle World angle of the shape (the .angle property will be ignored).
     */
    raycast: function(/*result, ray, position, angle*/){
        // To be implemented in each subclass
    },

    /**
     * Test if a point is inside this shape.
     * @method pointTest
     * @param {array} localPoint
     * @return {boolean}
     */
    pointTest: function(/*localPoint*/){ return false; },

    /**
     * Transform a world point to local shape space (assumed the shape is transformed by both itself and the body).
     * @method worldPointToLocal
     * @param {array} out
     * @param {array} worldPoint
     */
    worldPointToLocal: (function () {
        var shapeWorldPosition = vec2.create();
        return function (out, worldPoint) {
            var body = this.body;

            vec2.rotate(shapeWorldPosition, this.position, body.angle);
            vec2.add(shapeWorldPosition, shapeWorldPosition, body.position);

            vec2.toLocalFrame(out, worldPoint, shapeWorldPosition, this.body.angle + this.angle);
        };
    })()
};

./plane/Utils.js
/* global P2_ARRAY_TYPE */

module.exports = Utils;

/**
 * Misc utility functions
 * @class Utils
 * @constructor
 */
function Utils(){}

/**
 * Append the values in array b to the array a. See this for an explanation.
 * @method appendArray
 * @static
 * @param  {Array} a
 * @param  {Array} b
 */
Utils.appendArray = function(a,b){
    if (b.length < 150000) {
        a.push.apply(a, b);
    } else {
        for (var i = 0, len = b.length; i !== len; ++i) {
            a.push(b[i]);
        }
    }
};

/**
 * Garbage free Array.splice(). Does not allocate a new array.
 * @method splice
 * @static
 * @param  {Array} array
 * @param  {Number} index
 * @param  {Number} howmany
 */
Utils.splice = function(array,index,howmany){
    howmany = howmany || 1;
    for (var i=index, len=array.length-howmany; i < len; i++){
        array[i] = array[i + howmany];
    }
    array.length = len;
};

/**
 * Remove an element from an array, if the array contains the element.
 * @method arrayRemove
 * @static
 * @param  {Array} array
 * @param  {Number} element
 */
Utils.arrayRemove = function(array, element){
    var idx = array.indexOf(element);
    if(idx!==-1){
        Utils.splice(array, idx, 1);
    }
};

/**
 * The array type to use for internal numeric computations throughout the library. Float32Array is used if it is available, but falls back on Array. If you want to set array type manually, inject it via the global variable P2_ARRAY_TYPE. See example below.
 * @static
 * @property {function} ARRAY_TYPE
 * @example
 *
 *
 */
if(typeof P2_ARRAY_TYPE !== 'undefined') {
    Utils.ARRAY_TYPE = P2_ARRAY_TYPE;
} else if (typeof Float32Array !== 'undefined'){
    Utils.ARRAY_TYPE = Float32Array;
} else {
    Utils.ARRAY_TYPE = Array;
}

/**
 * Extend an object with the properties of another
 * @static
 * @method extend
 * @param  {object} a
 * @param  {object} b
 */
Utils.extend = function(a,b){
    for(var key in b){
        a[key] = b[key];
    }
};

/**
 * Shallow clone an object. Returns a new object instance with the same properties as the input instance.
 * @static
 * @method shallowClone
 * @param  {object} obj
 */
Utils.shallowClone = function(obj){
    var newObj = {};
    Utils.extend(newObj, obj);
    return newObj;
};

/**
 * Extend an options object with default values.
 * @deprecated Not used internally, will be removed.
 * @static
 * @method defaults
 * @param  {object} options The options object. May be falsy: in this case, a new object is created and returned.
 * @param  {object} defaults An object containing default values.
 * @return {object} The modified options object.
 */
Utils.defaults = function(options, defaults){
    console.warn('Utils.defaults is deprecated.');
    options = options || {};
    for(var key in defaults){
        if(!(key in options)){
            options[key] = defaults[key];
        }
    }
    return options;
};

./plane/vec2.js
/* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

/**
 * The vec2 object from glMatrix, with some extensions and some removed methods. See http://glmatrix.net.
 * @class vec2
 */

var vec2 = module.exports = {};

var Utils = require('./Utils');

/**
 * Make a cross product and only return the z component
 * @method crossLength
 * @static
 * @param  {Array} a
 * @param  {Array} b
 * @return {Number}
 */
vec2.crossLength = function(a,b){
    return a[0] * b[1] - a[1] * b[0];
};

/**
 * Cross product between a vector and the Z component of a vector
 * @method crossVZ
 * @static
 * @param  {Array} out
 * @param  {Array} vec
 * @param  {Number} zcomp
 * @return {Array}
 */
vec2.crossVZ = function(out, vec, zcomp){
    vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule
    vec2.scale(out,out,zcomp);      // Scale with z
    return out;
};

/**
 * Cross product between a vector and the Z component of a vector
 * @method crossZV
 * @static
 * @param  {Array} out
 * @param  {Number} zcomp
 * @param  {Array} vec
 * @return {Array}
 */
vec2.crossZV = function(out, zcomp, vec){
    vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule
    vec2.scale(out,out,zcomp);      // Scale with z
    return out;
};

/**
 * Rotate a vector by an angle
 * @method rotate
 * @static
 * @param  {Array} out
 * @param  {Array} a
 * @param  {Number} angle
 * @return {Array}
 */
vec2.rotate = function(out,a,angle){
    if(angle !== 0){
        var c = Math.cos(angle),
            s = Math.sin(angle),
            x = a[0],
            y = a[1];
        out[0] = c*x -s*y;
        out[1] = s*x +c*y;
    } else {
        out[0] = a[0];
        out[1] = a[1];
    }
    return out;
};

/**
 * Rotate a vector 90 degrees clockwise
 * @method rotate90cw
 * @static
 * @param  {Array} out
 * @param  {Array} a
 * @param  {Number} angle
 * @return {Array}
 */
vec2.rotate90cw = function(out, a) {
    var x = a[0];
    var y = a[1];
    out[0] = y;
    out[1] = -x;
    return out;
};

/**
 * Transform a point position to local frame.
 * @method toLocalFrame
 * @param  {Array} out
 * @param  {Array} worldPoint
 * @param  {Array} framePosition
 * @param  {Number} frameAngle
 * @return {Array}
 */
vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){
    var c = Math.cos(-frameAngle),
        s = Math.sin(-frameAngle),
        x = worldPoint[0] - framePosition[0],
        y = worldPoint[1] - framePosition[1];
    out[0] = c * x - s * y;
    out[1] = s * x + c * y;
    return out;
};

/**
 * Transform a point position to global frame.
 * @method toGlobalFrame
 * @param  {Array} out
 * @param  {Array} localPoint
 * @param  {Array} framePosition
 * @param  {Number} frameAngle
 */
vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){
    var c = Math.cos(frameAngle),
        s = Math.sin(frameAngle),
        x = localPoint[0],
        y = localPoint[1],
        addX = framePosition[0],
        addY = framePosition[1];
    out[0] = c * x - s * y + addX;
    out[1] = s * x + c * y + addY;
};

/**
 * Transform a vector to local frame.
 * @method vectorToLocalFrame
 * @param  {Array} out
 * @param  {Array} worldVector
 * @param  {Number} frameAngle
 * @return {Array}
 */
vec2.vectorToLocalFrame = function(out, worldVector, frameAngle){
    var c = Math.cos(-frameAngle),
        s = Math.sin(-frameAngle),
        x = worldVector[0],
        y = worldVector[1];
    out[0] = c*x -s*y;
    out[1] = s*x +c*y;
    return out;
};

/**
 * Transform a vector to global frame.
 * @method vectorToGlobalFrame
 * @param  {Array} out
 * @param  {Array} localVector
 * @param  {Number} frameAngle
 */
vec2.vectorToGlobalFrame = vec2.rotate;

/**
 * Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php
 * @method centroid
 * @static
 * @param  {Array} out
 * @param  {Array} a
 * @param  {Array} b
 * @param  {Array} c
 * @return  {Array} The "out" vector.
 */
vec2.centroid = function(out, a, b, c){
    vec2.add(out, a, b);
    vec2.add(out, out, c);
    vec2.scale(out, out, 1/3);
    return out;
};

/**
 * Creates a new, empty vec2
 * @static
 * @method create
 * @return {Array} a new 2D vector
 */
vec2.create = function() {
    var out = new Utils.ARRAY_TYPE(2);
    out[0] = 0;
    out[1] = 0;
    return out;
};

/**
 * Creates a new vec2 initialized with values from an existing vector
 * @static
 * @method clone
 * @param {Array} a vector to clone
 * @return {Array} a new 2D vector
 */
vec2.clone = function(a) {
    var out = new Utils.ARRAY_TYPE(2);
    out[0] = a[0];
    out[1] = a[1];
    return out;
};

/**
 * Creates a new vec2 initialized with the given values
 * @static
 * @method fromValues
 * @param {Number} x X component
 * @param {Number} y Y component
 * @return {Array} a new 2D vector
 */
vec2.fromValues = function(x, y) {
    var out = new Utils.ARRAY_TYPE(2);
    out[0] = x;
    out[1] = y;
    return out;
};

/**
 * Copy the values from one vec2 to another
 * @static
 * @method copy
 * @param {Array} out the receiving vector
 * @param {Array} a the source vector
 * @return {Array} out
 */
vec2.copy = function(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    return out;
};

/**
 * Set the components of a vec2 to the given values
 * @static
 * @method set
 * @param {Array} out the receiving vector
 * @param {Number} x X component
 * @param {Number} y Y component
 * @return {Array} out
 */
vec2.set = function(out, x, y) {
    out[0] = x;
    out[1] = y;
    return out;
};

/**
 * Adds two vec2's
 * @static
 * @method add
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.add = function(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    return out;
};

/**
 * Subtracts two vec2's
 * @static
 * @method subtract
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.subtract = function(out, a, b) {
    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];
    return out;
};

/**
 * Multiplies two vec2's
 * @static
 * @method multiply
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.multiply = function(out, a, b) {
    out[0] = a[0] * b[0];
    out[1] = a[1] * b[1];
    return out;
};

/**
 * Divides two vec2's
 * @static
 * @method divide
 * @param {Array} out the receiving vector
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Array} out
 */
vec2.divide = function(out, a, b) {
    out[0] = a[0] / b[0];
    out[1] = a[1] / b[1];
    return out;
};

/**
 * Scales a vec2 by a scalar number
 * @static
 * @method scale
 * @param {Array} out the receiving vector
 * @param {Array} a the vector to scale
 * @param {Number} b amount to scale the vector by
 * @return {Array} out
 */
vec2.scale = function(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1] * b;
    return out;
};

/**
 * Calculates the euclidian distance between two vec2's
 * @static
 * @method distance
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Number} distance between a and b
 */
vec2.distance = function(a, b) {
    var x = b[0] - a[0],
        y = b[1] - a[1];
    return Math.sqrt(x*x + y*y);
};

/**
 * Calculates the squared euclidian distance between two vec2's
 * @static
 * @method squaredDistance
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Number} squared distance between a and b
 */
vec2.squaredDistance = function(a, b) {
    var x = b[0] - a[0],
        y = b[1] - a[1];
    return x*x + y*y;
};

/**
 * Calculates the length of a vec2
 * @static
 * @method length
 * @param {Array} a vector to calculate length of
 * @return {Number} length of a
 */
vec2.length = function (a) {
    var x = a[0],
        y = a[1];
    return Math.sqrt(x*x + y*y);
};

/**
 * Calculates the squared length of a vec2
 * @static
 * @method squaredLength
 * @param {Array} a vector to calculate squared length of
 * @return {Number} squared length of a
 */
vec2.squaredLength = function (a) {
    var x = a[0],
        y = a[1];
    return x*x + y*y;
};

/**
 * Negates the components of a vec2
 * @static
 * @method negate
 * @param {Array} out the receiving vector
 * @param {Array} a vector to negate
 * @return {Array} out
 */
vec2.negate = function(out, a) {
    out[0] = -a[0];
    out[1] = -a[1];
    return out;
};

/**
 * Normalize a vec2
 * @static
 * @method normalize
 * @param {Array} out the receiving vector
 * @param {Array} a vector to normalize
 * @return {Array} out
 */
vec2.normalize = function(out, a) {
    var x = a[0],
        y = a[1];
    var len = x*x + y*y;
    if (len > 0) {
        //TODO: evaluate use of glm_invsqrt here?
        len = 1 / Math.sqrt(len);
        out[0] = a[0] * len;
        out[1] = a[1] * len;
    }
    return out;
};

/**
 * Calculates the dot product of two vec2's
 * @static
 * @method dot
 * @param {Array} a the first operand
 * @param {Array} b the second operand
 * @return {Number} dot product of a and b
 */
vec2.dot = function (a, b) {
    return a[0] * b[0] + a[1] * b[1];
};

/**
 * Returns a string representation of a vector
 * @static
 * @method str
 * @param {Array} vec vector to represent as a string
 * @return {String} string representation of the vector
 */
vec2.str = function (a) {
    return 'vec2(' + a[0] + ', ' + a[1] + ')';
};

/**
 * Linearly interpolate/mix two vectors.
 * @static
 * @method lerp
 * @param {Array} out
 * @param {Array} a First vector
 * @param {Array} b Second vector
 * @param {number} t Lerp factor
 */
vec2.lerp = function (out, a, b, t) {
    var ax = a[0],
        ay = a[1];
    out[0] = ax + t * (b[0] - ax);
    out[1] = ay + t * (b[1] - ay);
    return out;
};

/**
 * Reflect a vector along a normal.
 * @static
 * @method reflect
 * @param {Array} out
 * @param {Array} vector
 * @param {Array} normal
 */
vec2.reflect = function(out, vector, normal){
    var dot = vector[0] * normal[0] + vector[1] * normal[1];
    out[0] = vector[0] - 2 * normal[0] * dot;
    out[1] = vector[1] - 2 * normal[1] * dot;
};

/**
 * Get the intersection point between two line segments.
 * @static
 * @method getLineSegmentsIntersection
 * @param  {Array} out
 * @param  {Array} p0
 * @param  {Array} p1
 * @param  {Array} p2
 * @param  {Array} p3
 * @return {boolean} True if there was an intersection, otherwise false.
 */
vec2.getLineSegmentsIntersection = function(out, p0, p1, p2, p3) {
    var t = vec2.getLineSegmentsIntersectionFraction(p0, p1, p2, p3);
    if(t < 0){
        return false;
    } else {
        out[0] = p0[0] + (t * (p1[0] - p0[0]));
        out[1] = p0[1] + (t * (p1[1] - p0[1]));
        return true;
    }
};

/**
 * Get the intersection fraction between two line segments. If successful, the intersection is at p0 + t * (p1 - p0)
 * @static
 * @method getLineSegmentsIntersectionFraction
 * @param  {Array} p0
 * @param  {Array} p1
 * @param  {Array} p2
 * @param  {Array} p3
 * @return {number} A number between 0 and 1 if there was an intersection, otherwise -1.
 */
vec2.getLineSegmentsIntersectionFraction = function(p0, p1, p2, p3) {
    var s1_x = p1[0] - p0[0];
    var s1_y = p1[1] - p0[1];
    var s2_x = p3[0] - p2[0];
    var s2_y = p3[1] - p2[1];

    var s, t;
    s = (-s1_y * (p0[0] - p2[0]) + s1_x * (p0[1] - p2[1])) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0[1] - p2[1]) - s2_y * (p0[0] - p2[0])) / (-s2_x * s1_y + s1_x * s2_y);
    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { // Collision detected
        return t;
    }
    return -1; // No collision
};


./solver/EventEmitter.js
module.exports = EventEmitter;

/**
 * Base class for objects that dispatches events.
 * @class EventEmitter
 * @constructor
 * @example
 *     var emitter = new EventEmitter();
 *     emitter.on('myEvent', function(evt){
 *         console.log(evt.message);
 *     });
 *     emitter.emit({
 *         type: 'myEvent',
 *         message: 'Hello world!'
 *     });
 */
function EventEmitter() {
    this.tmpArray = [];
}

EventEmitter.prototype = {
    constructor: EventEmitter,

    /**
     * Add an event listener
     * @method on
     * @param  {String} type
     * @param  {Function} listener
     * @return {EventEmitter} The self object, for chainability.
     * @example
     *     emitter.on('myEvent', function(evt){
     *         console.log('myEvt was triggered!');
     *     });
     */
    on: function ( type, listener, context ) {
        listener.context = context || this;
        if ( this._listeners === undefined ){
            this._listeners = {};
        }
        var listeners = this._listeners;
        if ( listeners[ type ] === undefined ) {
            listeners[ type ] = [];
        }
        if ( listeners[ type ].indexOf( listener ) === - 1 ) {
            listeners[ type ].push( listener );
        }
        return this;
    },

    /**
     * Remove an event listener
     * @method off
     * @param  {String} type
     * @param  {Function} listener
     * @return {EventEmitter} The self object, for chainability.
     * @example
     *     emitter.on('myEvent', handler); // Add handler
     *     emitter.off('myEvent', handler); // Remove handler
     */
    off: function ( type, listener ) {
        var listeners = this._listeners;
        if(!listeners || !listeners[type]){
            return this;
        }
        var index = listeners[ type ].indexOf( listener );
        if ( index !== - 1 ) {
            listeners[ type ].splice( index, 1 );
        }
        return this;
    },

    /**
     * Check if an event listener is added
     * @method has
     * @param  {String} type
     * @param  {Function} listener
     * @return {Boolean}
     */
    has: function ( type, listener ) {
        if ( this._listeners === undefined ){
            return false;
        }
        var listeners = this._listeners;
        if(listener){
            if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
                return true;
            }
        } else {
            if ( listeners[ type ] !== undefined ) {
                return true;
            }
        }

        return false;
    },

    /**
     * Emit an event.
     * @method emit
     * @param  {Object} event
     * @param  {String} event.type
     * @return {EventEmitter} The self object, for chainability.
     * @example
     *     emitter.emit({
     *         type: 'myEvent',
     *         customData: 123
     *     });
     */
    emit: function ( event ) {
        if ( this._listeners === undefined ){
            return this;
        }
        var listeners = this._listeners;
        var listenerArray = listeners[ event.type ];
        if ( listenerArray !== undefined ) {
            event.target = this;

            // Need to copy the listener array, in case some listener was added/removed inside a listener
            var tmpArray = this.tmpArray;
            for (var i = 0, l = listenerArray.length; i < l; i++) {
                tmpArray[i] = listenerArray[i];
            }
            for (var i = 0, l = tmpArray.length; i < l; i++) {
                var listener = tmpArray[ i ];
                listener.call( listener.context, event );
            }
            tmpArray.length = 0;
        }
        return this;
    }
};

./solver/Solver.js
var EventEmitter = require('./EventEmitter');

module.exports = Solver;

/**
 * Base class for constraint solvers.
 * @class Solver
 * @constructor
 * @extends EventEmitter
 */
function Solver(options,type){
    options = options || {};

    EventEmitter.call(this);

    this.type = type;

    /**
     * Current equations in the solver.
     *
     * @property equations
     * @type {Array}
     */
    this.equations = [];

    /**
     * Function that is used to sort all equations before each solve.
     * @property equationSortFunction
     * @type {function|boolean}
     */
    this.equationSortFunction = options.equationSortFunction || false;
}
Solver.prototype = new EventEmitter();
Solver.prototype.constructor = Solver;

/**
 * Method to be implemented in each subclass
 * @method solve
 * @param  {Number} dt
 * @param  {World} world
 */
Solver.prototype.solve = function(/*dt,world*/){
    throw new Error("Solver.solve should be implemented by subclasses!");
};

/**
 * Sort all equations using the .equationSortFunction. Should be called by subclasses before solving.
 * @method sortEquations
 */
Solver.prototype.sortEquations = function(){
    if(this.equationSortFunction){
        this.equations.sort(this.equationSortFunction);
    }
};

/**
 * Add an equation to be solved.
 *
 * @method addEquation
 * @param {Equation} eq
 */
Solver.prototype.addEquation = function(eq){
    if(eq.enabled){
        this.equations.push(eq);
    }
};

/**
 * Add equations. Same as .addEquation, but this time the argument is an array of Equations
 *
 * @method addEquations
 * @param {Array} eqs
 */
Solver.prototype.addEquations = function(eqs){
    for(var i=0, N=eqs.length; i!==N; i++){
        var eq = eqs[i];
        if(eq.enabled){
            this.equations.push(eq);
        }
    }
};

/**
 * Remove an equation.
 *
 * @method removeEquation
 * @param {Equation} eq
 */
Solver.prototype.removeEquation = function(eq){
    var i = this.equations.indexOf(eq);
    if(i !== -1){
        this.equations.splice(i,1);
    }
};

/**
 * Remove all currently added equations.
 *
 * @method removeAllEquations
 */
Solver.prototype.removeAllEquations = function(){
    this.equations.length=0;
};

/**
 * Gauss-Seidel solver.
 * @property GS
 * @type {Number}
 * @static
 */
Solver.GS = 1;


./span/DomUtil.js
export default {
  /**
   * Creates and returns a new canvas. The opacity is by default set to 0
   *
   * @memberof Proton#Proton.DomUtil
   * @method createCanvas
   *
   * @param {String} $id the canvas' id
   * @param {Number} $width the canvas' width
   * @param {Number} $height the canvas' height
   * @param {String} [$position=absolute] the canvas' position, default is 'absolute'
   *
   * @return {Object}
   */
  createCanvas(id, width, height, position = "absolute") {
    const dom = document.createElement("canvas");

    dom.id = id;
    dom.width = width;
    dom.height = height;
    dom.style.opacity = 0;
    dom.style.position = position;
    this.transform(dom, -500, -500, 0, 0);

    return dom;
  },

  createDiv(id, width, height) {
    const dom = document.createElement("div");

    dom.id = id;
    dom.style.position = "absolute";
    this.resize(dom, width, height);

    return dom;
  },

  resize(dom, width, height) {
    dom.style.width = width + "px";
    dom.style.height = height + "px";
    dom.style.marginLeft = -width / 2 + "px";
    dom.style.marginTop = -height / 2 + "px";
  },

  /**
   * Adds a transform: translate(), scale(), rotate() to a given div dom for all browsers
   *
   * @memberof Proton#Proton.DomUtil
   * @method transform
   *
   * @param {HTMLDivElement} div
   * @param {Number} $x
   * @param {Number} $y
   * @param {Number} $scale
   * @param {Number} $rotate
   */
  transform(div, x, y, scale, rotate) {
    div.style.willChange = "transform";
    const transform = `translate(${x}px, ${y}px) scale(${scale}) rotate(${rotate}deg)`;
    this.css3(div, "transform", transform);
  },

  transform3d(div, x, y, scale, rotate) {
    div.style.willChange = "transform";
    const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale}) rotate(${rotate}deg)`;
    this.css3(div, "backfaceVisibility", "hidden");
    this.css3(div, "transform", transform);
  },

  css3(div, key, val) {
    const bkey = key.charAt(0).toUpperCase() + key.substr(1);

    div.style[`Webkit${bkey}`] = val;
    div.style[`Moz${bkey}`] = val;
    div.style[`O${bkey}`] = val;
    div.style[`ms${bkey}`] = val;
    div.style[`${key}`] = val;
  }
};

./span/ImgUtil.js
import WebGLUtil from "./WebGLUtil";
import DomUtil from "./DomUtil";

const imgsCache = {};
const canvasCache = {};
let canvasId = 0;

export default {
  /**
   * This will get the image data. It could be necessary to create a Proton.Zone.
   *
   * @memberof Proton#Proton.Util
   * @method getImageData
   *
   * @param {HTMLCanvasElement}   context any canvas, must be a 2dContext 'canvas.getContext('2d')'
   * @param {Object}              image   could be any dom image, e.g. document.getElementById('thisIsAnImgTag');
   * @param {Proton.Rectangle}    rect
   */
  getImageData(context, image, rect) {
    context.drawImage(image, rect.x, rect.y);
    const imagedata = context.getImageData(rect.x, rect.y, rect.width, rect.height);
    context.clearRect(rect.x, rect.y, rect.width, rect.height);

    return imagedata;
  },

  /**
   * @memberof Proton#Proton.Util
   * @method getImgFromCache
   *
   * @todo add description
   * @todo describe func
   *
   * @param {Mixed}               img
   * @param {Proton.Particle}     particle
   * @param {Boolean}             drawCanvas  set to true if a canvas should be saved into particle.data.canvas
   * @param {Boolean}             func
   */
  getImgFromCache(img, callback, param) {
    const src = typeof img === "string" ? img : img.src;

    if (imgsCache[src]) {
      callback(imgsCache[src], param);
    } else {
      const image = new Image();
      image.onload = e => {
        imgsCache[src] = e.target;
        callback(imgsCache[src], param);
      };

      image.src = src;
    }
  },

  getCanvasFromCache(img, callback, param) {
    const src = img.src;

    if (!canvasCache[src]) {
      const width = WebGLUtil.nhpot(img.width);
      const height = WebGLUtil.nhpot(img.height);

      const canvas = DomUtil.createCanvas(`proton_canvas_cache_${++canvasId}`, width, height);
      const context = canvas.getContext("2d");
      context.drawImage(img, 0, 0, img.width, img.height);

      canvasCache[src] = canvas;
    }

    callback && callback(canvasCache[src], param);

    return canvasCache[src];
  }
};

./span/MathUtil.js
const PI = 3.1415926;
const INFINITY = Infinity;

const MathUtil = {
  PI: PI,
  PIx2: PI * 2,
  PI_2: PI / 2,
  PI_180: PI / 180,
  N180_PI: 180 / PI,
  Infinity: -999,

  isInfinity(num) {
    return num === this.Infinity || num === INFINITY;
  },

  randomAToB(a, b, isInt = false) {
    if (!isInt) return a + Math.random() * (b - a);
    else return ((Math.random() * (b - a)) >> 0) + a;
  },

  randomFloating(center, f, isInt) {
    return this.randomAToB(center - f, center + f, isInt);
  },

  randomColor() {
    return "#" + ("00000" + ((Math.random() * 0x1000000) << 0).toString(16)).slice(-6);
  },

  randomZone(display) {},

  floor(num, k = 4) {
    const digits = Math.pow(10, k);
    return Math.floor(num * digits) / digits;
  },

  degreeTransform(a) {
    return (a * PI) / 180;
  },

  toColor16(num) {
    return `#${num.toString(16)}`;
  }
};

export default MathUtil;

./span/Span.js
import Util from "./Util";
import MathUtil from "./MathUtil";

export default class Span {
  constructor(a, b, center) {
    if (Util.isArray(a)) {
      this.isArray = true;
      this.a = a;
    } else {
      this.isArray = false;
      this.a = Util.initValue(a, 1);
      this.b = Util.initValue(b, this.a);
      this.center = Util.initValue(center, false);
    }
  }

  getValue(isInt = false) {
    if (this.isArray) {
      return Util.getRandFromArray(this.a);
    } else {
      if (!this.center) {
        return MathUtil.randomAToB(this.a, this.b, isInt);
      } else {
        return MathUtil.randomFloating(this.a, this.b, isInt);
      }
    }
  }

  /**
   * Returns a new Span object
   *
   * @memberof Proton#Proton.Util
   * @method setSpanValue
   *
   * @todo a, b and c should be 'Mixed' or 'Number'?
   *
   * @param {Mixed | Span} a
   * @param {Mixed}               b
   * @param {Mixed}               c
   *
   * @return {Span}
   */
  static setSpanValue(a, b, c) {
    if (a instanceof Span) {
      return a;
    } else {
      if (b === undefined) {
        return new Span(a);
      } else {
        if (c === undefined) return new Span(a, b);
        else return new Span(a, b, c);
      }
    }
  }

  /**
   * Returns the value from a Span, if the param is not a Span it will return the given parameter
   *
   * @memberof Proton#Proton.Util
   * @method getValue
   *
   * @param {Mixed | Span} pan
   *
   * @return {Mixed} the value of Span OR the parameter if it is not a Span
   */
  static getSpanValue(pan) {
    return pan instanceof Span ? pan.getValue() : pan;
  }
}

./span/Util.js
import ImgUtil from "./ImgUtil";

export default {
  /**
   * Returns the default if the value is null or undefined
   *
   * @memberof Proton#Proton.Util
   * @method initValue
   *
   * @param {Mixed} value a specific value, could be everything but null or undefined
   * @param {Mixed} defaults the default if the value is null or undefined
   */
  initValue(value, defaults) {
    value = value !== null && value !== undefined ? value : defaults;
    return value;
  },

  /**
   * Checks if the value is a valid array
   *
   * @memberof Proton#Proton.Util
   * @method isArray
   *
   * @param {Array} value Any array
   *
   * @returns {Boolean}
   */
  isArray(value) {
    return Object.prototype.toString.call(value) === "[object Array]";
  },

  /**
   * Destroyes the given array
   *
   * @memberof Proton#Proton.Util
   * @method emptyArray
   *
   * @param {Array} array Any array
   */
  emptyArray(arr) {
    if (arr) arr.length = 0;
  },

  toArray(arr) {
    return this.isArray(arr) ? arr : [arr];
  },

  sliceArray(arr1, index, arr2) {
    this.emptyArray(arr2);
    for (let i = index; i < arr1.length; i++) {
      arr2.push(arr1[i]);
    }
  },

  getRandFromArray(arr) {
    if (!arr) return null;
    return arr[Math.floor(arr.length * Math.random())];
  },

  /**
   * Destroyes the given object
   *
   * @memberof Proton#Proton.Util
   * @method emptyObject
   *
   * @param {Object} obj Any object
   */
  emptyObject(obj, ignore = null) {
    for (let key in obj) {
      if (ignore && ignore.indexOf(key) > -1) continue;
      delete obj[key];
    }
  },

  /**
   * Makes an instance of a class and binds the given array
   *
   * @memberof Proton#Proton.Util
   * @method classApply
   *
   * @param {Function} constructor A class to make an instance from
   * @param {Array} [args] Any array to bind it to the constructor
   *
   * @return {Object} The instance of constructor, optionally bind with args
   */
  classApply(constructor, args = null) {
    if (!args) {
      return new constructor();
    } else {
      const FactoryFunc = constructor.bind.apply(constructor, [null].concat(args));
      return new FactoryFunc();
    }
  },

  /**
   * This will get the image data. It could be necessary to create a Proton.Zone.
   *
   * @memberof Proton#Proton.Util
   * @method getImageData
   *
   * @param {HTMLCanvasElement}   context any canvas, must be a 2dContext 'canvas.getContext('2d')'
   * @param {Object}              image   could be any dom image, e.g. document.getElementById('thisIsAnImgTag');
   * @param {Proton.Rectangle}    rect
   */
  getImageData(context, image, rect) {
    return ImgUtil.getImageData(context, image, rect);
  },

  destroyAll(arr, param = null) {
    let i = arr.length;

    while (i--) {
      try {
        arr[i].destroy(param);
      } catch (e) {}

      delete arr[i];
    }

    arr.length = 0;
  },

  assign(target, source) {
    if (typeof Object.assign !== "function") {
      for (let key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      }

      return target;
    } else {
      return Object.assign(target, source);
    }
  }
};

./span/WebGLUtil.js
export default {
  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method ipot
   *
   * @todo add description
   * @todo add length description
   *
   * @param {Number} length
   *
   * @return {Boolean}
   */
  ipot(length) {
    return (length & (length - 1)) === 0;
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method nhpot
   *
   * @todo add description
   * @todo add length description
   *
   * @param {Number} length
   *
   * @return {Number}
   */
  nhpot(length) {
    --length;
    for (let i = 1; i < 32; i <<= 1) {
      length = length | (length >> i);
    }

    return length + 1;
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method makeTranslation
   *
   * @todo add description
   * @todo add tx, ty description
   * @todo add return description
   *
   * @param {Number} tx either 0 or 1
   * @param {Number} ty either 0 or 1
   *
   * @return {Object}
   */
  makeTranslation(tx, ty) {
    return [1, 0, 0, 0, 1, 0, tx, ty, 1];
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method makeRotation
   *
   * @todo add description
   * @todo add return description
   *
   * @param {Number} angleInRadians
   *
   * @return {Object}
   */
  makeRotation(angleInRadians) {
    let c = Math.cos(angleInRadians);
    let s = Math.sin(angleInRadians);

    return [c, -s, 0, s, c, 0, 0, 0, 1];
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method makeScale
   *
   * @todo add description
   * @todo add tx, ty description
   * @todo add return description
   *
   * @param {Number} sx either 0 or 1
   * @param {Number} sy either 0 or 1
   *
   * @return {Object}
   */
  makeScale(sx, sy) {
    return [sx, 0, 0, 0, sy, 0, 0, 0, 1];
  },

  /**
   * @memberof Proton#Proton.WebGLUtil
   * @method matrixMultiply
   *
   * @todo add description
   * @todo add a, b description
   * @todo add return description
   *
   * @param {Object} a
   * @param {Object} b
   *
   * @return {Object}
   */
  matrixMultiply(a, b) {
    let a00 = a[0 * 3 + 0];
    let a01 = a[0 * 3 + 1];
    let a02 = a[0 * 3 + 2];
    let a10 = a[1 * 3 + 0];
    let a11 = a[1 * 3 + 1];
    let a12 = a[1 * 3 + 2];
    let a20 = a[2 * 3 + 0];
    let a21 = a[2 * 3 + 1];
    let a22 = a[2 * 3 + 2];
    let b00 = b[0 * 3 + 0];
    let b01 = b[0 * 3 + 1];
    let b02 = b[0 * 3 + 2];
    let b10 = b[1 * 3 + 0];
    let b11 = b[1 * 3 + 1];
    let b12 = b[1 * 3 + 2];
    let b20 = b[2 * 3 + 0];
    let b21 = b[2 * 3 + 1];
    let b22 = b[2 * 3 + 2];

    return [
      a00 * b00 + a01 * b10 + a02 * b20,
      a00 * b01 + a01 * b11 + a02 * b21,
      a00 * b02 + a01 * b12 + a02 * b22,
      a10 * b00 + a11 * b10 + a12 * b20,
      a10 * b01 + a11 * b11 + a12 * b21,
      a10 * b02 + a11 * b12 + a12 * b22,
      a20 * b00 + a21 * b10 + a22 * b20,
      a20 * b01 + a21 * b11 + a22 * b21,
      a20 * b02 + a21 * b12 + a22 * b22
    ];
  }
};


./synergy/attribute.js
import { isPrimitive, typeOf } from "./helpers.js"

const pascalToKebab = (string) =>
  string.replace(/[\w]([A-Z])/g, function (m) {
    return m[0] + "-" + m[1].toLowerCase()
  })

const kebabToPascal = (string) =>
  string.replace(/[\w]-([\w])/g, function (m) {
    return m[0] + m[2].toUpperCase()
  })

const parseStyles = (value) => {
  let type = typeof value

  if (type === "string")
    return value.split(";").reduce((o, value) => {
      const [k, v] = value.split(":").map((v) => v.trim())
      if (k) o[k] = v
      return o
    }, {})

  if (type === "object") return value

  return {}
}

const joinStyles = (value) =>
  Object.entries(value)
    .map(([k, v]) => `${k}: ${v};`)
    .join(" ")

const convertStyles = (o) =>
  Object.keys(o).reduce((a, k) => {
    a[pascalToKebab(k)] = o[k]
    return a
  }, {})

export const applyAttribute = (node, name, value) => {
  if (name === "style") {
    value = joinStyles(
      convertStyles({
        ...parseStyles(node.getAttribute("style")),
        ...parseStyles(value),
      })
    )
  } else if (name === "class") {
    switch (typeOf(value)) {
      case "Array":
        value = value.join(" ")
        break
      case "Object":
        value = Object.keys(value)
          .reduce((a, k) => {
            if (value[k]) a.push(k)
            return a
          }, [])
          .join(" ")
        break
    }
  } else if (!isPrimitive(value)) {
    return (node[kebabToPascal(name)] = value)
  }

  name = pascalToKebab(name)

  if (typeof value === "boolean") {
    if (name.startsWith("aria-")) {
      value = "" + value
    } else if (value) {
      value = ""
    }
  }

  let current = node.getAttribute(name)

  if (value === current) return

  if (typeof value === "string" || typeof value === "number") {
    node.setAttribute(name, value)
  } else {
    node.removeAttribute(name)
  }
}

./synergy/context.js
import { getValueAtPath, isPrimitive } from "./helpers.js"

const handler = ({ path, identifier, key, index, i, k }) => ({
  get(target, property) {
    let x = getValueAtPath(path, target)

    // x === the collection

    if (property === identifier) {
      for (let n in x) {
        let v = x[n]
        if (key) {
          if (v[key] === k) return v
        } else {
          if (n == i) return v
        }
      }
    }

    if (property === index) {
      for (let n in x) {
        let v = x[n]
        if (key) {
          if (v[key] === k) return n
        } else {
          if (n == i) return n
        }
      }
    }

    let t = key ? x.find((v) => v[key] === k) : x?.[i]
    if (t?.hasOwnProperty?.(property)) return t[property]

    return Reflect.get(...arguments)
  },
  set(target, property, value) {
    let x = getValueAtPath(path, target)
    let t = key ? x.find((v) => v[key] === k) : x[i]
    if (t && !isPrimitive(t)) {
      t[property] = value
      return true
    }

    return Reflect.set(...arguments)
  },
})

export const createContext = (v = []) => {
  let context = v
  return {
    get: () => context,
    push: (v) => context.push(v),
    wrap: (state) => {
      return context.reduce(
        (target, ctx) => new Proxy(target, handler(ctx)),
        state
      )
    },
  }
}

./synergy/css.js
function nextWord(css, count) {
  return css.slice(count - 1).split(/[\s+|\n+|,]/)[0]
}

function nextOpenBrace(css, count) {
  let index = css.slice(count - 1).indexOf("{")
  if (index > -1) {
    return count + index
  }
}

export function prefixSelectors(prefix, css) {
  let insideBlock = false
  let look = true
  let output = ""
  let count = 0
  let skip = false

  for (let char of css) {
    if (char === "@" && nextWord(css, count + 1) === "@media") {
      skip = nextOpenBrace(css, count)
    }

    if (skip) {
      if (skip === count) skip = false
    }

    if (!skip) {
      if (char === "}") {
        insideBlock = false
        look = true
      } else if (char === ",") {
        look = true
      } else if (char === "{") {
        insideBlock = true
      } else if (look && !insideBlock && !char.match(/\s/)) {
        let w = nextWord(css, count + 1)

        // console.log({ w })

        if (
          w !== prefix &&
          w.charAt(0) !== "@" &&
          w.charAt(0) !== ":" &&
          w.charAt(0) !== "*" &&
          w !== "html" &&
          w !== "body"
        ) {
          output += prefix + " "
        }
        look = false
      }
    }
    output += char
    count += 1
  }

  return output
}

export function appendStyles(name, css) {
  if (document.querySelector(`style#${name}`)) return

  const el = document.createElement("style")
  el.id = name
  el.innerHTML = prefixSelectors(name, css)
  document.head.appendChild(el)
}

./synergy/helpers.js
export const wrapToken = (v) => {
  v = v.trim()
  if (v.startsWith("{{")) return v
  return `{{${v}}}`
}

export const last = (v = []) => v[v.length - 1]

export const isWhitespace = (node) => {
  return node.nodeType === node.TEXT_NODE && node.nodeValue.trim() === ""
}

export const walk = (node, callback, deep = true) => {
  if (!node) return
  // if (node.matches?.(`script[type="application/synergy"]`))
  //   return walk(node.nextSibling, callback, deep)
  if (!isWhitespace(node)) {
    let v = callback(node)
    if (v === false) return
    if (v?.nodeName) return walk(v, callback, deep)
  }
  if (deep) walk(node.firstChild, callback, deep)
  walk(node.nextSibling, callback, deep)
}

const transformBrackets = (str = "") => {
  let parts = str.split(/(\[[^\]]+\])/).filter((v) => v)
  return parts.reduce((a, part) => {
    let v = part.charAt(0) === "[" ? "." + part.replace(/\./g, ":") : part
    return a + v
  }, "")
}

const getTarget = (path, target) => {
  let parts = transformBrackets(path)
    .split(".")
    .map((k) => {
      if (k.charAt(0) === "[") {
        let p = k.slice(1, -1).replace(/:/g, ".")
        return getValueAtPath(p, target)
      } else {
        return k
      }
    })

  let t =
    parts.slice(0, -1).reduce((o, k) => {
      return o && o[k]
    }, target) || target
  return [t, last(parts)]
}

export const getValueAtPath = (path, target) => {
  let [a, b] = getTarget(path, target)
  let v = a?.[b]
  if (typeof v === "function") return v.bind(a)
  return v
}

export const setValueAtPath = (path, value, target) => {
  let [a, b] = getTarget(path, target)
  return (a[b] = value)
}

export const fragmentFromTemplate = (v) => {
  if (typeof v === "string") {
    if (v.charAt(0) === "#") {
      v = document.querySelector(v)
    } else {
      let tpl = document.createElement("template")
      tpl.innerHTML = v.trim()
      return tpl.content.cloneNode(true)
    }
  }
  if (v.nodeName === "TEMPLATE") return v.cloneNode(true).content
  if (v.nodeName === "defs") return v.firstElementChild.cloneNode(true)
}

export const debounce = (fn) => {
  let wait = false
  let invoke = false
  return () => {
    if (wait) {
      invoke = true
    } else {
      wait = true
      fn()
      requestAnimationFrame(() => {
        if (invoke) fn()
        wait = false
      })
    }
  }
}

export const isPrimitive = (v) => v === null || typeof v !== "object"

export const typeOf = (v) =>
  Object.prototype.toString.call(v).match(/\s(.+[^\]])/)[1]

export const pascalToKebab = (string) =>
  string.replace(/[\w]([A-Z])/g, function (m) {
    return m[0] + "-" + m[1].toLowerCase()
  })

export const kebabToPascal = (string) =>
  string.replace(/[\w]-([\w])/g, function (m) {
    return m[0] + m[2].toUpperCase()
  })

export const applyAttribute = (node, name, value) => {
  name = pascalToKebab(name)

  if (typeof value === "boolean") {
    if (name.startsWith("aria-")) {
      value = "" + value
    } else if (value) {
      value = ""
    }
  }

  if (typeof value === "string" || typeof value === "number") {
    node.setAttribute(name, value)
  } else {
    node.removeAttribute(name)
  }
}

export const attributeToProp = (k, v) => {
  let name = kebabToPascal(k)
  if (v === "") v = true
  if (k.startsWith("aria-")) {
    if (v === "true") v = true
    if (v === "false") v = false
  }
  return {
    name,
    value: v,
  }
}

export function getDataScript(node) {
  return node.querySelector(
    `script[type="application/synergy"][id="${node.nodeName}"]`
  )
}

export function createDataScript(node) {
  let ds = document.createElement("script")
  ds.setAttribute("type", "application/synergy")
  ds.setAttribute("id", node.nodeName)
  node.append(ds)
  return ds
}

./synergy/partial.js
import { appendStyles } from "./css.js"

export const partials = {}

export const partial = (name, html, css) => {
  if (css) appendStyles(name, css)
  partials[name.toUpperCase()] = html
}