Best JavaScript code snippet using playwright-internal
Body.js
Source:Body.js  
1/**2* The `Matter.Body` module contains methods for creating and manipulating body models.3* A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`.4* Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the module `Matter.Bodies`.5*6* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).7* @class Body8*/9var Body = {};10module.exports = Body;11var Vertices = require('../geometry/Vertices');12var Vector = require('../geometry/Vector');13var Sleeping = require('../core/Sleeping');14var Render = require('../render/Render');15var Common = require('../core/Common');16var Bounds = require('../geometry/Bounds');17var Axes = require('../geometry/Axes');18(function() {19    Body._inertiaScale = 4;20    Body._nextCollidingGroupId = 1;21    Body._nextNonCollidingGroupId = -1;22    Body._nextCategory = 0x0001;23    /**24     * Creates a new rigid body model. The options parameter is an object that specifies any properties you wish to override the defaults.25     * All properties have default values, and many are pre-calculated automatically based on other properties.26     * Vertices must be specified in clockwise order.27     * See the properties section below for detailed information on what you can pass via the `options` object.28     * @method create29     * @param {} options30     * @return {body} body31     */32    Body.create = function(options) {33        var defaults = {34            id: Common.nextId(),35            type: 'body',36            label: 'Body',37            parts: [],38            plugin: {},39            angle: 0,40            vertices: Vertices.fromPath('L 0 0 L 40 0 L 40 40 L 0 40'),41            position: { x: 0, y: 0 },42            force: { x: 0, y: 0 },43            torque: 0,44            positionImpulse: { x: 0, y: 0 },45            constraintImpulse: { x: 0, y: 0, angle: 0 },46            totalContacts: 0,47            speed: 0,48            angularSpeed: 0,49            velocity: { x: 0, y: 0 },50            angularVelocity: 0,51            isSensor: false,52            isStatic: false,53            isSleeping: false,54            motion: 0,55            sleepThreshold: 60,56            density: 0.001,57            restitution: 0,58            friction: 0.1,59            frictionStatic: 0.5,60            frictionAir: 0.01,61            collisionFilter: {62                category: 0x0001,63                mask: 0xFFFFFFFF,64                group: 065            },66            slop: 0.05,67            timeScale: 1,68            render: {69                visible: true,70                opacity: 1,71                sprite: {72                    xScale: 1,73                    yScale: 1,74                    xOffset: 0,75                    yOffset: 076                },77                lineWidth: 078            },79            events: null,80            bounds: null,81            chamfer: null,82            circleRadius: 0,83            positionPrev: null,84            anglePrev: 0,85            parent: null,86            axes: null,87            area: 0,88            mass: 0,89            inertia: 0,90            _original: null91        };92        var body = Common.extend(defaults, options);93        _initProperties(body, options);94        return body;95    };96    /**97     * Returns the next unique group index for which bodies will collide.98     * If `isNonColliding` is `true`, returns the next unique group index for which bodies will _not_ collide.99     * See `body.collisionFilter` for more information.100     * @method nextGroup101     * @param {bool} [isNonColliding=false]102     * @return {Number} Unique group index103     */104    Body.nextGroup = function(isNonColliding) {105        if (isNonColliding)106            return Body._nextNonCollidingGroupId--;107        return Body._nextCollidingGroupId++;108    };109    /**110     * Returns the next unique category bitfield (starting after the initial default category `0x0001`).111     * There are 32 available. See `body.collisionFilter` for more information.112     * @method nextCategory113     * @return {Number} Unique category bitfield114     */115    Body.nextCategory = function() {116        Body._nextCategory = Body._nextCategory << 1;117        return Body._nextCategory;118    };119    /**120     * Initialises body properties.121     * @method _initProperties122     * @private123     * @param {body} body124     * @param {} [options]125     */126    var _initProperties = function(body, options) {127        options = options || {};128        // init required properties (order is important)129        Body.set(body, {130            bounds: body.bounds || Bounds.create(body.vertices),131            positionPrev: body.positionPrev || Vector.clone(body.position),132            anglePrev: body.anglePrev || body.angle,133            vertices: body.vertices,134            parts: body.parts || [body],135            isStatic: body.isStatic,136            isSleeping: body.isSleeping,137            parent: body.parent || body138        });139        Vertices.rotate(body.vertices, body.angle, body.position);140        Axes.rotate(body.axes, body.angle);141        Bounds.update(body.bounds, body.vertices, body.velocity);142        // allow options to override the automatically calculated properties143        Body.set(body, {144            axes: options.axes || body.axes,145            area: options.area || body.area,146            mass: options.mass || body.mass,147            inertia: options.inertia || body.inertia148        });149        // render properties150        var defaultFillStyle = (body.isStatic ? '#2e2b44' : Common.choose(['#006BA6', '#0496FF', '#FFBC42', '#D81159', '#8F2D56'])),151            defaultStrokeStyle = '#000';152        body.render.fillStyle = body.render.fillStyle || defaultFillStyle;153        body.render.strokeStyle = body.render.strokeStyle || defaultStrokeStyle;154        body.render.sprite.xOffset += -(body.bounds.min.x - body.position.x) / (body.bounds.max.x - body.bounds.min.x);155        body.render.sprite.yOffset += -(body.bounds.min.y - body.position.y) / (body.bounds.max.y - body.bounds.min.y);156    };157    /**158     * Given a property and a value (or map of), sets the property(s) on the body, using the appropriate setter functions if they exist.159     * Prefer to use the actual setter functions in performance critical situations.160     * @method set161     * @param {body} body162     * @param {} settings A property name (or map of properties and values) to set on the body.163     * @param {} value The value to set if `settings` is a single property name.164     */165    Body.set = function(body, settings, value) {166        var property;167        if (typeof settings === 'string') {168            property = settings;169            settings = {};170            settings[property] = value;171        }172        for (property in settings) {173            if (!Object.prototype.hasOwnProperty.call(settings, property))174                continue;175            value = settings[property];176            switch (property) {177            case 'isStatic':178                Body.setStatic(body, value);179                break;180            case 'isSleeping':181                Sleeping.set(body, value);182                break;183            case 'mass':184                Body.setMass(body, value);185                break;186            case 'density':187                Body.setDensity(body, value);188                break;189            case 'inertia':190                Body.setInertia(body, value);191                break;192            case 'vertices':193                Body.setVertices(body, value);194                break;195            case 'position':196                Body.setPosition(body, value);197                break;198            case 'angle':199                Body.setAngle(body, value);200                break;201            case 'velocity':202                Body.setVelocity(body, value);203                break;204            case 'angularVelocity':205                Body.setAngularVelocity(body, value);206                break;207            case 'parts':208                Body.setParts(body, value);209                break;210            case 'centre':211                Body.setCentre(body, value);212                break;213            default:214                body[property] = value;215            }216        }217    };218    /**219     * Sets the body as static, including isStatic flag and setting mass and inertia to Infinity.220     * @method setStatic221     * @param {body} body222     * @param {bool} isStatic223     */224    Body.setStatic = function(body, isStatic) {225        for (var i = 0; i < body.parts.length; i++) {226            var part = body.parts[i];227            part.isStatic = isStatic;228            if (isStatic) {229                part._original = {230                    restitution: part.restitution,231                    friction: part.friction,232                    mass: part.mass,233                    inertia: part.inertia,234                    density: part.density,235                    inverseMass: part.inverseMass,236                    inverseInertia: part.inverseInertia237                };238                part.restitution = 0;239                part.friction = 1;240                part.mass = part.inertia = part.density = Infinity;241                part.inverseMass = part.inverseInertia = 0;242                part.positionPrev.x = part.position.x;243                part.positionPrev.y = part.position.y;244                part.anglePrev = part.angle;245                part.angularVelocity = 0;246                part.speed = 0;247                part.angularSpeed = 0;248                part.motion = 0;249            } else if (part._original) {250                part.restitution = part._original.restitution;251                part.friction = part._original.friction;252                part.mass = part._original.mass;253                part.inertia = part._original.inertia;254                part.density = part._original.density;255                part.inverseMass = part._original.inverseMass;256                part.inverseInertia = part._original.inverseInertia;257                part._original = null;258            }259        }260    };261    /**262     * Sets the mass of the body. Inverse mass, density and inertia are automatically updated to reflect the change.263     * @method setMass264     * @param {body} body265     * @param {number} mass266     */267    Body.setMass = function(body, mass) {268        var moment = body.inertia / (body.mass / 6);269        body.inertia = moment * (mass / 6);270        body.inverseInertia = 1 / body.inertia;271        body.mass = mass;272        body.inverseMass = 1 / body.mass;273        body.density = body.mass / body.area;274    };275    /**276     * Sets the density of the body. Mass and inertia are automatically updated to reflect the change.277     * @method setDensity278     * @param {body} body279     * @param {number} density280     */281    Body.setDensity = function(body, density) {282        Body.setMass(body, density * body.area);283        body.density = density;284    };285    /**286     * Sets the moment of inertia (i.e. second moment of area) of the body. 287     * Inverse inertia is automatically updated to reflect the change. Mass is not changed.288     * @method setInertia289     * @param {body} body290     * @param {number} inertia291     */292    Body.setInertia = function(body, inertia) {293        body.inertia = inertia;294        body.inverseInertia = 1 / body.inertia;295    };296    /**297     * Sets the body's vertices and updates body properties accordingly, including inertia, area and mass (with respect to `body.density`).298     * Vertices will be automatically transformed to be orientated around their centre of mass as the origin.299     * They are then automatically translated to world space based on `body.position`.300     *301     * The `vertices` argument should be passed as an array of `Matter.Vector` points (or a `Matter.Vertices` array).302     * Vertices must form a convex hull, concave hulls are not supported.303     *304     * @method setVertices305     * @param {body} body306     * @param {vector[]} vertices307     */308    Body.setVertices = function(body, vertices) {309        // change vertices310        if (vertices[0].body === body) {311            body.vertices = vertices;312        } else {313            body.vertices = Vertices.create(vertices, body);314        }315        // update properties316        body.axes = Axes.fromVertices(body.vertices);317        body.area = Vertices.area(body.vertices);318        Body.setMass(body, body.density * body.area);319        // orient vertices around the centre of mass at origin (0, 0)320        var centre = Vertices.centre(body.vertices);321        Vertices.translate(body.vertices, centre, -1);322        // update inertia while vertices are at origin (0, 0)323        Body.setInertia(body, Body._inertiaScale * Vertices.inertia(body.vertices, body.mass));324        // update geometry325        Vertices.translate(body.vertices, body.position);326        Bounds.update(body.bounds, body.vertices, body.velocity);327    };328    /**329     * Sets the parts of the `body` and updates mass, inertia and centroid.330     * Each part will have its parent set to `body`.331     * By default the convex hull will be automatically computed and set on `body`, unless `autoHull` is set to `false.`332     * Note that this method will ensure that the first part in `body.parts` will always be the `body`.333     * @method setParts334     * @param {body} body335     * @param [body] parts336     * @param {bool} [autoHull=true]337     */338    Body.setParts = function(body, parts, autoHull) {339        var i;340        // add all the parts, ensuring that the first part is always the parent body341        parts = parts.slice(0);342        body.parts.length = 0;343        body.parts.push(body);344        body.parent = body;345        for (i = 0; i < parts.length; i++) {346            var part = parts[i];347            if (part !== body) {348                part.parent = body;349                body.parts.push(part);350            }351        }352        if (body.parts.length === 1)353            return;354        autoHull = typeof autoHull !== 'undefined' ? autoHull : true;355        // find the convex hull of all parts to set on the parent body356        if (autoHull) {357            var vertices = [];358            for (i = 0; i < parts.length; i++) {359                vertices = vertices.concat(parts[i].vertices);360            }361            Vertices.clockwiseSort(vertices);362            var hull = Vertices.hull(vertices),363                hullCentre = Vertices.centre(hull);364            Body.setVertices(body, hull);365            Vertices.translate(body.vertices, hullCentre);366        }367        // sum the properties of all compound parts of the parent body368        var total = Body._totalProperties(body);369        body.area = total.area;370        body.parent = body;371        body.position.x = total.centre.x;372        body.position.y = total.centre.y;373        body.positionPrev.x = total.centre.x;374        body.positionPrev.y = total.centre.y;375        Body.setMass(body, total.mass);376        Body.setInertia(body, total.inertia);377        Body.setPosition(body, total.centre);378    };379    /**380     * Set the centre of mass of the body. 381     * The `centre` is a vector in world-space unless `relative` is set, in which case it is a translation.382     * The centre of mass is the point the body rotates about and can be used to simulate non-uniform density.383     * This is equal to moving `body.position` but not the `body.vertices`.384     * Invalid if the `centre` falls outside the body's convex hull.385     * @method setCentre386     * @param {body} body387     * @param {vector} centre388     * @param {bool} relative389     */390    Body.setCentre = function(body, centre, relative) {391        if (!relative) {392            body.positionPrev.x = centre.x - (body.position.x - body.positionPrev.x);393            body.positionPrev.y = centre.y - (body.position.y - body.positionPrev.y);394            body.position.x = centre.x;395            body.position.y = centre.y;396        } else {397            body.positionPrev.x += centre.x;398            body.positionPrev.y += centre.y;399            body.position.x += centre.x;400            body.position.y += centre.y;401        }402    };403    /**404     * Sets the position of the body instantly. Velocity, angle, force etc. are unchanged.405     * @method setPosition406     * @param {body} body407     * @param {vector} position408     */409    Body.setPosition = function(body, position) {410        var delta = Vector.sub(position, body.position);411        body.positionPrev.x += delta.x;412        body.positionPrev.y += delta.y;413        for (var i = 0; i < body.parts.length; i++) {414            var part = body.parts[i];415            part.position.x += delta.x;416            part.position.y += delta.y;417            Vertices.translate(part.vertices, delta);418            Bounds.update(part.bounds, part.vertices, body.velocity);419        }420    };421    /**422     * Sets the angle of the body instantly. Angular velocity, position, force etc. are unchanged.423     * @method setAngle424     * @param {body} body425     * @param {number} angle426     */427    Body.setAngle = function(body, angle) {428        var delta = angle - body.angle;429        body.anglePrev += delta;430        for (var i = 0; i < body.parts.length; i++) {431            var part = body.parts[i];432            part.angle += delta;433            Vertices.rotate(part.vertices, delta, body.position);434            Axes.rotate(part.axes, delta);435            Bounds.update(part.bounds, part.vertices, body.velocity);436            if (i > 0) {437                Vector.rotateAbout(part.position, delta, body.position, part.position);438            }439        }440    };441    /**442     * Sets the linear velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`.443     * @method setVelocity444     * @param {body} body445     * @param {vector} velocity446     */447    Body.setVelocity = function(body, velocity) {448        body.positionPrev.x = body.position.x - velocity.x;449        body.positionPrev.y = body.position.y - velocity.y;450        body.velocity.x = velocity.x;451        body.velocity.y = velocity.y;452        body.speed = Vector.magnitude(body.velocity);453    };454    /**455     * Sets the angular velocity of the body instantly. Position, angle, force etc. are unchanged. See also `Body.applyForce`.456     * @method setAngularVelocity457     * @param {body} body458     * @param {number} velocity459     */460    Body.setAngularVelocity = function(body, velocity) {461        body.anglePrev = body.angle - velocity;462        body.angularVelocity = velocity;463        body.angularSpeed = Math.abs(body.angularVelocity);464    };465    /**466     * Moves a body by a given vector relative to its current position, without imparting any velocity.467     * @method translate468     * @param {body} body469     * @param {vector} translation470     */471    Body.translate = function(body, translation) {472        Body.setPosition(body, Vector.add(body.position, translation));473    };474    /**475     * Rotates a body by a given angle relative to its current angle, without imparting any angular velocity.476     * @method rotate477     * @param {body} body478     * @param {number} rotation479     * @param {vector} [point]480     */481    Body.rotate = function(body, rotation, point) {482        if (!point) {483            Body.setAngle(body, body.angle + rotation);484        } else {485            var cos = Math.cos(rotation),486                sin = Math.sin(rotation),487                dx = body.position.x - point.x,488                dy = body.position.y - point.y;489                490            Body.setPosition(body, {491                x: point.x + (dx * cos - dy * sin),492                y: point.y + (dx * sin + dy * cos)493            });494            Body.setAngle(body, body.angle + rotation);495        }496    };497    /**498     * Scales the body, including updating physical properties (mass, area, axes, inertia), from a world-space point (default is body centre).499     * @method scale500     * @param {body} body501     * @param {number} scaleX502     * @param {number} scaleY503     * @param {vector} [point]504     */505    Body.scale = function(body, scaleX, scaleY, point) {506        var totalArea = 0,507            totalInertia = 0;508        point = point || body.position;509        for (var i = 0; i < body.parts.length; i++) {510            var part = body.parts[i];511            // scale vertices512            Vertices.scale(part.vertices, scaleX, scaleY, point);513            // update properties514            part.axes = Axes.fromVertices(part.vertices);515            part.area = Vertices.area(part.vertices);516            Body.setMass(part, body.density * part.area);517            // update inertia (requires vertices to be at origin)518            Vertices.translate(part.vertices, { x: -part.position.x, y: -part.position.y });519            Body.setInertia(part, Body._inertiaScale * Vertices.inertia(part.vertices, part.mass));520            Vertices.translate(part.vertices, { x: part.position.x, y: part.position.y });521            if (i > 0) {522                totalArea += part.area;523                totalInertia += part.inertia;524            }525            // scale position526            part.position.x = point.x + (part.position.x - point.x) * scaleX;527            part.position.y = point.y + (part.position.y - point.y) * scaleY;528            // update bounds529            Bounds.update(part.bounds, part.vertices, body.velocity);530        }531        // handle parent body532        if (body.parts.length > 1) {533            body.area = totalArea;534            if (!body.isStatic) {535                Body.setMass(body, body.density * totalArea);536                Body.setInertia(body, totalInertia);537            }538        }539        // handle circles540        if (body.circleRadius) { 541            if (scaleX === scaleY) {542                body.circleRadius *= scaleX;543            } else {544                // body is no longer a circle545                body.circleRadius = null;546            }547        }548    };549    /**550     * Performs a simulation step for the given `body`, including updating position and angle using Verlet integration.551     * @method update552     * @param {body} body553     * @param {number} deltaTime554     * @param {number} timeScale555     * @param {number} correction556     */557    Body.update = function(body, deltaTime, timeScale, correction) {558        var deltaTimeSquared = Math.pow(deltaTime * timeScale * body.timeScale, 2);559        // from the previous step560        var frictionAir = 1 - body.frictionAir * timeScale * body.timeScale,561            velocityPrevX = body.position.x - body.positionPrev.x,562            velocityPrevY = body.position.y - body.positionPrev.y;563        // update velocity with Verlet integration564        body.velocity.x = (velocityPrevX * frictionAir * correction) + (body.force.x / body.mass) * deltaTimeSquared;565        body.velocity.y = (velocityPrevY * frictionAir * correction) + (body.force.y / body.mass) * deltaTimeSquared;566        body.positionPrev.x = body.position.x;567        body.positionPrev.y = body.position.y;568        body.position.x += body.velocity.x;569        body.position.y += body.velocity.y;570        // update angular velocity with Verlet integration571        body.angularVelocity = ((body.angle - body.anglePrev) * frictionAir * correction) + (body.torque / body.inertia) * deltaTimeSquared;572        body.anglePrev = body.angle;573        body.angle += body.angularVelocity;574        // track speed and acceleration575        body.speed = Vector.magnitude(body.velocity);576        body.angularSpeed = Math.abs(body.angularVelocity);577        // transform the body geometry578        for (var i = 0; i < body.parts.length; i++) {579            var part = body.parts[i];580            Vertices.translate(part.vertices, body.velocity);581            582            if (i > 0) {583                part.position.x += body.velocity.x;584                part.position.y += body.velocity.y;585            }586            if (body.angularVelocity !== 0) {587                Vertices.rotate(part.vertices, body.angularVelocity, body.position);588                Axes.rotate(part.axes, body.angularVelocity);589                if (i > 0) {590                    Vector.rotateAbout(part.position, body.angularVelocity, body.position, part.position);591                }592            }593            Bounds.update(part.bounds, part.vertices, body.velocity);594        }595    };596    /**597     * Applies a force to a body from a given world-space position, including resulting torque.598     * @method applyForce599     * @param {body} body600     * @param {vector} position601     * @param {vector} force602     */603    Body.applyForce = function(body, position, force) {604        body.force.x += force.x;605        body.force.y += force.y;606        var offset = { x: position.x - body.position.x, y: position.y - body.position.y };607        body.torque += offset.x * force.y - offset.y * force.x;608    };609    /**610     * Returns the sums of the properties of all compound parts of the parent body.611     * @method _totalProperties612     * @private613     * @param {body} body614     * @return {}615     */616    Body._totalProperties = function(body) {617        // from equations at:618        // https://ecourses.ou.edu/cgi-bin/ebook.cgi?doc=&topic=st&chap_sec=07.2&page=theory619        // http://output.to/sideway/default.asp?qno=121100087620        var properties = {621            mass: 0,622            area: 0,623            inertia: 0,624            centre: { x: 0, y: 0 }625        };626        // sum the properties of all compound parts of the parent body627        for (var i = body.parts.length === 1 ? 0 : 1; i < body.parts.length; i++) {628            var part = body.parts[i],629                mass = part.mass !== Infinity ? part.mass : 1;630            properties.mass += mass;631            properties.area += part.area;632            properties.inertia += part.inertia;633            properties.centre = Vector.add(properties.centre, Vector.mult(part.position, mass));634        }635        properties.centre = Vector.div(properties.centre, properties.mass);636        return properties;637    };638    /*639    *640    *  Events Documentation641    *642    */643    /**644    * Fired when a body starts sleeping (where `this` is the body).645    *646    * @event sleepStart647    * @this {body} The body that has started sleeping648    * @param {} event An event object649    * @param {} event.source The source object of the event650    * @param {} event.name The name of the event651    */652    /**653    * Fired when a body ends sleeping (where `this` is the body).654    *655    * @event sleepEnd656    * @this {body} The body that has ended sleeping657    * @param {} event An event object658    * @param {} event.source The source object of the event659    * @param {} event.name The name of the event660    */661    /*662    *663    *  Properties Documentation664    *665    */666    /**667     * An integer `Number` uniquely identifying number generated in `Body.create` by `Common.nextId`.668     *669     * @property id670     * @type number671     */672    /**673     * A `String` denoting the type of object.674     *675     * @property type676     * @type string677     * @default "body"678     * @readOnly679     */680    /**681     * An arbitrary `String` name to help the user identify and manage bodies.682     *683     * @property label684     * @type string685     * @default "Body"686     */687    /**688     * An array of bodies that make up this body. 689     * The first body in the array must always be a self reference to the current body instance.690     * All bodies in the `parts` array together form a single rigid compound body.691     * Parts are allowed to overlap, have gaps or holes or even form concave bodies.692     * Parts themselves should never be added to a `World`, only the parent body should be.693     * Use `Body.setParts` when setting parts to ensure correct updates of all properties.694     *695     * @property parts696     * @type body[]697     */698    /**699     * An object reserved for storing plugin-specific properties.700     *701     * @property plugin702     * @type {}703     */704    /**705     * A self reference if the body is _not_ a part of another body.706     * Otherwise this is a reference to the body that this is a part of.707     * See `body.parts`.708     *709     * @property parent710     * @type body711     */712    /**713     * A `Number` specifying the angle of the body, in radians.714     *715     * @property angle716     * @type number717     * @default 0718     */719    /**720     * An array of `Vector` objects that specify the convex hull of the rigid body.721     * These should be provided about the origin `(0, 0)`. E.g.722     *723     *     [{ x: 0, y: 0 }, { x: 25, y: 50 }, { x: 50, y: 0 }]724     *725     * When passed via `Body.create`, the vertices are translated relative to `body.position` (i.e. world-space, and constantly updated by `Body.update` during simulation).726     * The `Vector` objects are also augmented with additional properties required for efficient collision detection. 727     *728     * Other properties such as `inertia` and `bounds` are automatically calculated from the passed vertices (unless provided via `options`).729     * Concave hulls are not currently supported. The module `Matter.Vertices` contains useful methods for working with vertices.730     *731     * @property vertices732     * @type vector[]733     */734    /**735     * A `Vector` that specifies the current world-space position of the body.736     *737     * @property position738     * @type vector739     * @default { x: 0, y: 0 }740     */741    /**742     * A `Vector` that specifies the force to apply in the current step. It is zeroed after every `Body.update`. See also `Body.applyForce`.743     *744     * @property force745     * @type vector746     * @default { x: 0, y: 0 }747     */748    /**749     * A `Number` that specifies the torque (turning force) to apply in the current step. It is zeroed after every `Body.update`.750     *751     * @property torque752     * @type number753     * @default 0754     */755    /**756     * A `Number` that _measures_ the current speed of the body after the last `Body.update`. It is read-only and always positive (it's the magnitude of `body.velocity`).757     *758     * @readOnly759     * @property speed760     * @type number761     * @default 0762     */763    /**764     * A `Number` that _measures_ the current angular speed of the body after the last `Body.update`. It is read-only and always positive (it's the magnitude of `body.angularVelocity`).765     *766     * @readOnly767     * @property angularSpeed768     * @type number769     * @default 0770     */771    /**772     * A `Vector` that _measures_ the current velocity of the body after the last `Body.update`. It is read-only. 773     * If you need to modify a body's velocity directly, you should either apply a force or simply change the body's `position` (as the engine uses position-Verlet integration).774     *775     * @readOnly776     * @property velocity777     * @type vector778     * @default { x: 0, y: 0 }779     */780    /**781     * A `Number` that _measures_ the current angular velocity of the body after the last `Body.update`. It is read-only. 782     * If you need to modify a body's angular velocity directly, you should apply a torque or simply change the body's `angle` (as the engine uses position-Verlet integration).783     *784     * @readOnly785     * @property angularVelocity786     * @type number787     * @default 0788     */789    /**790     * A flag that indicates whether a body is considered static. A static body can never change position or angle and is completely fixed.791     * If you need to set a body as static after its creation, you should use `Body.setStatic` as this requires more than just setting this flag.792     *793     * @property isStatic794     * @type boolean795     * @default false796     */797    /**798     * A flag that indicates whether a body is a sensor. Sensor triggers collision events, but doesn't react with colliding body physically.799     *800     * @property isSensor801     * @type boolean802     * @default false803     */804    /**805     * A flag that indicates whether the body is considered sleeping. A sleeping body acts similar to a static body, except it is only temporary and can be awoken.806     * If you need to set a body as sleeping, you should use `Sleeping.set` as this requires more than just setting this flag.807     *808     * @property isSleeping809     * @type boolean810     * @default false811     */812    /**813     * A `Number` that _measures_ the amount of movement a body currently has (a combination of `speed` and `angularSpeed`). It is read-only and always positive.814     * It is used and updated by the `Matter.Sleeping` module during simulation to decide if a body has come to rest.815     *816     * @readOnly817     * @property motion818     * @type number819     * @default 0820     */821    /**822     * A `Number` that defines the number of updates in which this body must have near-zero velocity before it is set as sleeping by the `Matter.Sleeping` module (if sleeping is enabled by the engine).823     *824     * @property sleepThreshold825     * @type number826     * @default 60827     */828    /**829     * A `Number` that defines the density of the body, that is its mass per unit area.830     * If you pass the density via `Body.create` the `mass` property is automatically calculated for you based on the size (area) of the object.831     * This is generally preferable to simply setting mass and allows for more intuitive definition of materials (e.g. rock has a higher density than wood).832     *833     * @property density834     * @type number835     * @default 0.001836     */837    /**838     * A `Number` that defines the mass of the body, although it may be more appropriate to specify the `density` property instead.839     * If you modify this value, you must also modify the `body.inverseMass` property (`1 / mass`).840     *841     * @property mass842     * @type number843     */844    /**845     * A `Number` that defines the inverse mass of the body (`1 / mass`).846     * If you modify this value, you must also modify the `body.mass` property.847     *848     * @property inverseMass849     * @type number850     */851    /**852     * A `Number` that defines the moment of inertia (i.e. second moment of area) of the body.853     * It is automatically calculated from the given convex hull (`vertices` array) and density in `Body.create`.854     * If you modify this value, you must also modify the `body.inverseInertia` property (`1 / inertia`).855     *856     * @property inertia857     * @type number858     */859    /**860     * A `Number` that defines the inverse moment of inertia of the body (`1 / inertia`).861     * If you modify this value, you must also modify the `body.inertia` property.862     *863     * @property inverseInertia864     * @type number865     */866    /**867     * A `Number` that defines the restitution (elasticity) of the body. The value is always positive and is in the range `(0, 1)`.868     * A value of `0` means collisions may be perfectly inelastic and no bouncing may occur. 869     * A value of `0.8` means the body may bounce back with approximately 80% of its kinetic energy.870     * Note that collision response is based on _pairs_ of bodies, and that `restitution` values are _combined_ with the following formula:871     *872     *     Math.max(bodyA.restitution, bodyB.restitution)873     *874     * @property restitution875     * @type number876     * @default 0877     */878    /**879     * A `Number` that defines the friction of the body. The value is always positive and is in the range `(0, 1)`.880     * A value of `0` means that the body may slide indefinitely.881     * A value of `1` means the body may come to a stop almost instantly after a force is applied.882     *883     * The effects of the value may be non-linear. 884     * High values may be unstable depending on the body.885     * The engine uses a Coulomb friction model including static and kinetic friction.886     * Note that collision response is based on _pairs_ of bodies, and that `friction` values are _combined_ with the following formula:887     *888     *     Math.min(bodyA.friction, bodyB.friction)889     *890     * @property friction891     * @type number892     * @default 0.1893     */894    /**895     * A `Number` that defines the static friction of the body (in the Coulomb friction model). 896     * A value of `0` means the body will never 'stick' when it is nearly stationary and only dynamic `friction` is used.897     * The higher the value (e.g. `10`), the more force it will take to initially get the body moving when nearly stationary.898     * This value is multiplied with the `friction` property to make it easier to change `friction` and maintain an appropriate amount of static friction.899     *900     * @property frictionStatic901     * @type number902     * @default 0.5903     */904    /**905     * A `Number` that defines the air friction of the body (air resistance). 906     * A value of `0` means the body will never slow as it moves through space.907     * The higher the value, the faster a body slows when moving through space.908     * The effects of the value are non-linear. 909     *910     * @property frictionAir911     * @type number912     * @default 0.01913     */914    /**915     * An `Object` that specifies the collision filtering properties of this body.916     *917     * Collisions between two bodies will obey the following rules:918     * - If the two bodies have the same non-zero value of `collisionFilter.group`,919     *   they will always collide if the value is positive, and they will never collide920     *   if the value is negative.921     * - If the two bodies have different values of `collisionFilter.group` or if one922     *   (or both) of the bodies has a value of 0, then the category/mask rules apply as follows:923     *924     * Each body belongs to a collision category, given by `collisionFilter.category`. This925     * value is used as a bit field and the category should have only one bit set, meaning that926     * the value of this property is a power of two in the range [1, 2^31]. Thus, there are 32927     * different collision categories available.928     *929     * Each body also defines a collision bitmask, given by `collisionFilter.mask` which specifies930     * the categories it collides with (the value is the bitwise AND value of all these categories).931     *932     * Using the category/mask rules, two bodies `A` and `B` collide if each includes the other's933     * category in its mask, i.e. `(categoryA & maskB) !== 0` and `(categoryB & maskA) !== 0`934     * are both true.935     *936     * @property collisionFilter937     * @type object938     */939    /**940     * An Integer `Number`, that specifies the collision group this body belongs to.941     * See `body.collisionFilter` for more information.942     *943     * @property collisionFilter.group944     * @type object945     * @default 0946     */947    /**948     * A bit field that specifies the collision category this body belongs to.949     * The category value should have only one bit set, for example `0x0001`.950     * This means there are up to 32 unique collision categories available.951     * See `body.collisionFilter` for more information.952     *953     * @property collisionFilter.category954     * @type object955     * @default 1956     */957    /**958     * A bit mask that specifies the collision categories this body may collide with.959     * See `body.collisionFilter` for more information.960     *961     * @property collisionFilter.mask962     * @type object963     * @default -1964     */965    /**966     * A `Number` that specifies a tolerance on how far a body is allowed to 'sink' or rotate into other bodies.967     * Avoid changing this value unless you understand the purpose of `slop` in physics engines.968     * The default should generally suffice, although very large bodies may require larger values for stable stacking.969     *970     * @property slop971     * @type number972     * @default 0.05973     */974    /**975     * A `Number` that allows per-body time scaling, e.g. a force-field where bodies inside are in slow-motion, while others are at full speed.976     *977     * @property timeScale978     * @type number979     * @default 1980     */981    /**982     * An `Object` that defines the rendering properties to be consumed by the module `Matter.Render`.983     *984     * @property render985     * @type object986     */987    /**988     * A flag that indicates if the body should be rendered.989     *990     * @property render.visible991     * @type boolean992     * @default true993     */994    /**995     * Sets the opacity to use when rendering.996     *997     * @property render.opacity998     * @type number999     * @default 11000    */1001    /**1002     * An `Object` that defines the sprite properties to use when rendering, if any.1003     *1004     * @property render.sprite1005     * @type object1006     */1007    /**1008     * An `String` that defines the path to the image to use as the sprite texture, if any.1009     *1010     * @property render.sprite.texture1011     * @type string1012     */1013     1014    /**1015     * A `Number` that defines the scaling in the x-axis for the sprite, if any.1016     *1017     * @property render.sprite.xScale1018     * @type number1019     * @default 11020     */1021    /**1022     * A `Number` that defines the scaling in the y-axis for the sprite, if any.1023     *1024     * @property render.sprite.yScale1025     * @type number1026     * @default 11027     */1028    /**1029      * A `Number` that defines the offset in the x-axis for the sprite (normalised by texture width).1030      *1031      * @property render.sprite.xOffset1032      * @type number1033      * @default 01034      */1035    /**1036      * A `Number` that defines the offset in the y-axis for the sprite (normalised by texture height).1037      *1038      * @property render.sprite.yOffset1039      * @type number1040      * @default 01041      */1042    /**1043     * A `Number` that defines the line width to use when rendering the body outline (if a sprite is not defined).1044     * A value of `0` means no outline will be rendered.1045     *1046     * @property render.lineWidth1047     * @type number1048     * @default 01049     */1050    /**1051     * A `String` that defines the fill style to use when rendering the body (if a sprite is not defined).1052     * It is the same as when using a canvas, so it accepts CSS style property values.1053     *1054     * @property render.fillStyle1055     * @type string1056     * @default a random colour1057     */1058    /**1059     * A `String` that defines the stroke style to use when rendering the body outline (if a sprite is not defined).1060     * It is the same as when using a canvas, so it accepts CSS style property values.1061     *1062     * @property render.strokeStyle1063     * @type string1064     * @default a random colour1065     */1066    /**1067     * An array of unique axis vectors (edge normals) used for collision detection.1068     * These are automatically calculated from the given convex hull (`vertices` array) in `Body.create`.1069     * They are constantly updated by `Body.update` during the simulation.1070     *1071     * @property axes1072     * @type vector[]1073     */1074     1075    /**1076     * A `Number` that _measures_ the area of the body's convex hull, calculated at creation by `Body.create`.1077     *1078     * @property area1079     * @type string1080     * @default 1081     */1082    /**1083     * A `Bounds` object that defines the AABB region for the body.1084     * It is automatically calculated from the given convex hull (`vertices` array) in `Body.create` and constantly updated by `Body.update` during simulation.1085     *1086     * @property bounds1087     * @type bounds1088     */...TilemapCollision.js
Source:TilemapCollision.js  
1/**2* @author       Richard Davey <rich@photonstorm.com>3* @copyright    2016 Photon Storm Ltd.4* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}5*/6/**7* The Arcade Physics Tile map collision methods.8*9* @class Phaser.Physics.Arcade.TilemapCollision10* @constructor11*/12Phaser.Physics.Arcade.TilemapCollision = function () {};13Phaser.Physics.Arcade.TilemapCollision.prototype = {14    /**15    * @property {number} TILE_BIAS - A value added to the delta values during collision with tiles. Adjust this if you get tunneling.16    */17    TILE_BIAS: 16,18    /**19    * An internal function. Use Phaser.Physics.Arcade.collide instead.20    *21    * @method Phaser.Physics.Arcade#collideSpriteVsTilemapLayer22    * @private23    * @param {Phaser.Sprite} sprite - The sprite to check.24    * @param {Phaser.TilemapLayer} tilemapLayer - The layer to check.25    * @param {function} collideCallback - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them.26    * @param {function} processCallback - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.27    * @param {object} callbackContext - The context in which to run the callbacks.28    * @param {boolean} overlapOnly - Just run an overlap or a full collision.29    */30    collideSpriteVsTilemapLayer: function (sprite, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) {31        if (!sprite.body)32        {33            return;34        }35        var mapData = tilemapLayer.getTiles(36            sprite.body.position.x - sprite.body.tilePadding.x - tilemapLayer.getTileOffsetX(),37            sprite.body.position.y - sprite.body.tilePadding.y - tilemapLayer.getTileOffsetY(),38            sprite.body.width + sprite.body.tilePadding.x,39            sprite.body.height + sprite.body.tilePadding.y,40            false, false);41        if (mapData.length === 0)42        {43            return;44        }45        for (var i = 0; i < mapData.length; i++)46        {47            if (processCallback)48            {49                if (processCallback.call(callbackContext, sprite, mapData[i]))50                {51                    if (this.separateTile(i, sprite.body, mapData[i], tilemapLayer, overlapOnly))52                    {53                        this._total++;54                        if (collideCallback)55                        {56                            collideCallback.call(callbackContext, sprite, mapData[i]);57                        }58                    }59                }60            }61            else62            {63                if (this.separateTile(i, sprite.body, mapData[i], tilemapLayer, overlapOnly))64                {65                    this._total++;66                    if (collideCallback)67                    {68                        collideCallback.call(callbackContext, sprite, mapData[i]);69                    }70                }71            }72        }73    },74    /**75    * An internal function. Use Phaser.Physics.Arcade.collide instead.76    *77    * @private78    * @method Phaser.Physics.Arcade#collideGroupVsTilemapLayer79    * @param {Phaser.Group} group - The Group to check.80    * @param {Phaser.TilemapLayer} tilemapLayer - The layer to check.81    * @param {function} collideCallback - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them.82    * @param {function} processCallback - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.83    * @param {object} callbackContext - The context in which to run the callbacks.84    * @param {boolean} overlapOnly - Just run an overlap or a full collision.85    */86    collideGroupVsTilemapLayer: function (group, tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly) {87        if (group.length === 0)88        {89            return;90        }91        for (var i = 0; i < group.children.length; i++)92        {93            if (group.children[i].exists)94            {95                this.collideSpriteVsTilemapLayer(group.children[i], tilemapLayer, collideCallback, processCallback, callbackContext, overlapOnly);96            }97        }98    },99    /**100    * The core separation function to separate a physics body and a tile.101    *102    * @private103    * @method Phaser.Physics.Arcade#separateTile104    * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.105    * @param {Phaser.Tile} tile - The tile to collide against.106    * @param {Phaser.TilemapLayer} tilemapLayer - The tilemapLayer to collide against.107    * @return {boolean} Returns true if the body was separated, otherwise false.108    */109    separateTile: function (i, body, tile, tilemapLayer, overlapOnly) {110        if (!body.enable)111        {112            return false;113        }114        115        var tilemapLayerOffsetX = tilemapLayer.getTileOffsetX();116        var tilemapLayerOffsetY = tilemapLayer.getTileOffsetY();117        //  We re-check for collision in case body was separated in a previous step118        if (!tile.intersects((body.position.x - tilemapLayerOffsetX), (body.position.y - tilemapLayerOffsetY), (body.right - tilemapLayerOffsetX), (body.bottom - tilemapLayerOffsetY)))119        {120            //  no collision so bail out (separated in a previous step)121            return false;122        }123        else if (overlapOnly)124        {125            //  There is an overlap, and we don't need to separate. Bail.126            return true;127        }128        //  They overlap. Any custom callbacks?129        //  A local callback always takes priority over a layer level callback130        if (tile.collisionCallback && !tile.collisionCallback.call(tile.collisionCallbackContext, body.sprite, tile))131        {132            //  If it returns true then we can carry on, otherwise we should abort.133            return false;134        }135        else if (typeof tile.layer.callbacks !== 'undefined' && tile.layer.callbacks[tile.index] && !tile.layer.callbacks[tile.index].callback.call(tile.layer.callbacks[tile.index].callbackContext, body.sprite, tile))136        {137            //  If it returns true then we can carry on, otherwise we should abort.138            return false;139        }140        //  We don't need to go any further if this tile doesn't actually separate141        if (!tile.faceLeft && !tile.faceRight && !tile.faceTop && !tile.faceBottom)142        {143            //   This could happen if the tile was meant to be collided with re: a callback, but otherwise isn't needed for separation144            return false;145        }146        var ox = 0;147        var oy = 0;148        var minX = 0;149        var minY = 1;150        if (body.deltaAbsX() > body.deltaAbsY())151        {152            //  Moving faster horizontally, check X axis first153            minX = -1;154        }155        else if (body.deltaAbsX() < body.deltaAbsY())156        {157            //  Moving faster vertically, check Y axis first158            minY = -1;159        }160        if (body.deltaX() !== 0 && body.deltaY() !== 0 && (tile.faceLeft || tile.faceRight) && (tile.faceTop || tile.faceBottom))161        {162            //  We only need do this if both axis have checking faces AND we're moving in both directions163            minX = Math.min(Math.abs((body.position.x - tilemapLayerOffsetX) - tile.right), Math.abs((body.right - tilemapLayerOffsetX) - tile.left));164            minY = Math.min(Math.abs((body.position.y - tilemapLayerOffsetY) - tile.bottom), Math.abs((body.bottom - tilemapLayerOffsetY) - tile.top));165        }166        if (minX < minY)167        {168            if (tile.faceLeft || tile.faceRight)169            {170                ox = this.tileCheckX(body, tile, tilemapLayer);171                //  That's horizontal done, check if we still intersects? If not then we can return now172                if (ox !== 0 && !tile.intersects((body.position.x - tilemapLayerOffsetX), (body.position.y - tilemapLayerOffsetY), (body.right - tilemapLayerOffsetX), (body.bottom - tilemapLayerOffsetY)))173                {174                    return true;175                }176            }177            if (tile.faceTop || tile.faceBottom)178            {179                oy = this.tileCheckY(body, tile, tilemapLayer);180            }181        }182        else183        {184            if (tile.faceTop || tile.faceBottom)185            {186                oy = this.tileCheckY(body, tile, tilemapLayer);187                //  That's vertical done, check if we still intersects? If not then we can return now188                if (oy !== 0 && !tile.intersects((body.position.x - tilemapLayerOffsetX), (body.position.y - tilemapLayerOffsetY), (body.right - tilemapLayerOffsetX), (body.bottom - tilemapLayerOffsetY)))189                {190                    return true;191                }192            }193            if (tile.faceLeft || tile.faceRight)194            {195                ox = this.tileCheckX(body, tile, tilemapLayer);196            }197        }198        return (ox !== 0 || oy !== 0);199    },200    /**201    * Check the body against the given tile on the X axis.202    *203    * @private204    * @method Phaser.Physics.Arcade#tileCheckX205    * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.206    * @param {Phaser.Tile} tile - The tile to check.207    * @param {Phaser.TilemapLayer} tilemapLayer - The tilemapLayer to collide against.208    * @return {number} The amount of separation that occurred.209    */210    tileCheckX: function (body, tile, tilemapLayer) {211        var ox = 0;212        var tilemapLayerOffsetX = tilemapLayer.getTileOffsetX();213        if (body.deltaX() < 0 && !body.blocked.left && tile.collideRight && body.checkCollision.left)214        {215            //  Body is moving LEFT216            if (tile.faceRight && (body.x - tilemapLayerOffsetX) < tile.right)217            {218                ox = (body.x - tilemapLayerOffsetX) - tile.right;219                if (ox < -this.TILE_BIAS)220                {221                    ox = 0;222                }223            }224        }225        else if (body.deltaX() > 0 && !body.blocked.right && tile.collideLeft && body.checkCollision.right)226        {227            //  Body is moving RIGHT228            if (tile.faceLeft && (body.right - tilemapLayerOffsetX) > tile.left)229            {230                ox = (body.right - tilemapLayerOffsetX) - tile.left;231                if (ox > this.TILE_BIAS)232                {233                    ox = 0;234                }235            }236        }237        if (ox !== 0)238        {239            if (body.customSeparateX)240            {241                body.overlapX = ox;242            }243            else244            {245                this.processTileSeparationX(body, ox);246            }247        }248        return ox;249    },250    /**251    * Check the body against the given tile on the Y axis.252    *253    * @private254    * @method Phaser.Physics.Arcade#tileCheckY255    * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.256    * @param {Phaser.Tile} tile - The tile to check.257    * @param {Phaser.TilemapLayer} tilemapLayer - The tilemapLayer to collide against.258    * @return {number} The amount of separation that occurred.259    */260    tileCheckY: function (body, tile, tilemapLayer) {261        var oy = 0;262        var tilemapLayerOffsetY = tilemapLayer.getTileOffsetY();263        if (body.deltaY() < 0 && !body.blocked.up && tile.collideDown && body.checkCollision.up)264        {265            //  Body is moving UP266            if (tile.faceBottom && (body.y - tilemapLayerOffsetY) < tile.bottom)267            {268                oy = (body.y - tilemapLayerOffsetY) - tile.bottom;269                if (oy < -this.TILE_BIAS)270                {271                    oy = 0;272                }273            }274        }275        else if (body.deltaY() > 0 && !body.blocked.down && tile.collideUp && body.checkCollision.down)276        {277            //  Body is moving DOWN278            if (tile.faceTop && (body.bottom - tilemapLayerOffsetY) > tile.top)279            {280                oy = (body.bottom - tilemapLayerOffsetY) - tile.top;281                if (oy > this.TILE_BIAS)282                {283                    oy = 0;284                }285            }286        }287        if (oy !== 0)288        {289            if (body.customSeparateY)290            {291                body.overlapY = oy;292            }293            else294            {295                this.processTileSeparationY(body, oy);296            }297        }298        return oy;299    },300    /**301    * Internal function to process the separation of a physics body from a tile.302    *303    * @private304    * @method Phaser.Physics.Arcade#processTileSeparationX305    * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.306    * @param {number} x - The x separation amount.307    */308    processTileSeparationX: function (body, x) {309        if (x < 0)310        {311            body.blocked.left = true;312        }313        else if (x > 0)314        {315            body.blocked.right = true;316        }317        body.position.x -= x;318        if (body.bounce.x === 0)319        {320            body.velocity.x = 0;321        }322        else323        {324            body.velocity.x = -body.velocity.x * body.bounce.x;325        }326    },327    /**328    * Internal function to process the separation of a physics body from a tile.329    *330    * @private331    * @method Phaser.Physics.Arcade#processTileSeparationY332    * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.333    * @param {number} y - The y separation amount.334    */335    processTileSeparationY: function (body, y) {336        if (y < 0)337        {338            body.blocked.up = true;339        }340        else if (y > 0)341        {342            body.blocked.down = true;343        }344        body.position.y -= y;345        if (body.bounce.y === 0)346        {347            body.velocity.y = 0;348        }349        else350        {351            body.velocity.y = -body.velocity.y * body.bounce.y;352        }353    }354};355//  Merge this with the Arcade Physics prototype...Composites.js
Source:Composites.js  
1/**2* The `Matter.Composites` module contains factory methods for creating composite bodies3* with commonly used configurations (such as stacks and chains).4*5* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).6*7* @class Composites8*/9var Composites = {};10module.exports = Composites;11var Composite = require('../body/Composite');12var Constraint = require('../constraint/Constraint');13var Common = require('../core/Common');14var Body = require('../body/Body');15var Bodies = require('./Bodies');16(function() {17    /**18     * Create a new composite containing bodies created in the callback in a grid arrangement.19     * This function uses the body's bounds to prevent overlaps.20     * @method stack21     * @param {number} xx22     * @param {number} yy23     * @param {number} columns24     * @param {number} rows25     * @param {number} columnGap26     * @param {number} rowGap27     * @param {function} callback28     * @return {composite} A new composite containing objects created in the callback29     */30    Composites.stack = function(xx, yy, columns, rows, columnGap, rowGap, callback) {31        var stack = Composite.create({ label: 'Stack' }),32            x = xx,33            y = yy,34            lastBody,35            i = 0;36        for (var row = 0; row < rows; row++) {37            var maxHeight = 0;38            39            for (var column = 0; column < columns; column++) {40                var body = callback(x, y, column, row, lastBody, i);41                    42                if (body) {43                    var bodyHeight = body.bounds.max.y - body.bounds.min.y,44                        bodyWidth = body.bounds.max.x - body.bounds.min.x; 45                    if (bodyHeight > maxHeight)46                        maxHeight = bodyHeight;47                    48                    Body.translate(body, { x: bodyWidth * 0.5, y: bodyHeight * 0.5 });49                    x = body.bounds.max.x + columnGap;50                    Composite.addBody(stack, body);51                    52                    lastBody = body;53                    i += 1;54                } else {55                    x += columnGap;56                }57            }58            59            y += maxHeight + rowGap;60            x = xx;61        }62        return stack;63    };64    65    /**66     * Chains all bodies in the given composite together using constraints.67     * @method chain68     * @param {composite} composite69     * @param {number} xOffsetA70     * @param {number} yOffsetA71     * @param {number} xOffsetB72     * @param {number} yOffsetB73     * @param {object} options74     * @return {composite} A new composite containing objects chained together with constraints75     */76    Composites.chain = function(composite, xOffsetA, yOffsetA, xOffsetB, yOffsetB, options) {77        var bodies = composite.bodies;78        79        for (var i = 1; i < bodies.length; i++) {80            var bodyA = bodies[i - 1],81                bodyB = bodies[i],82                bodyAHeight = bodyA.bounds.max.y - bodyA.bounds.min.y,83                bodyAWidth = bodyA.bounds.max.x - bodyA.bounds.min.x, 84                bodyBHeight = bodyB.bounds.max.y - bodyB.bounds.min.y,85                bodyBWidth = bodyB.bounds.max.x - bodyB.bounds.min.x;86        87            var defaults = {88                bodyA: bodyA,89                pointA: { x: bodyAWidth * xOffsetA, y: bodyAHeight * yOffsetA },90                bodyB: bodyB,91                pointB: { x: bodyBWidth * xOffsetB, y: bodyBHeight * yOffsetB }92            };93            94            var constraint = Common.extend(defaults, options);95        96            Composite.addConstraint(composite, Constraint.create(constraint));97        }98        composite.label += ' Chain';99        100        return composite;101    };102    /**103     * Connects bodies in the composite with constraints in a grid pattern, with optional cross braces.104     * @method mesh105     * @param {composite} composite106     * @param {number} columns107     * @param {number} rows108     * @param {boolean} crossBrace109     * @param {object} options110     * @return {composite} The composite containing objects meshed together with constraints111     */112    Composites.mesh = function(composite, columns, rows, crossBrace, options) {113        var bodies = composite.bodies,114            row,115            col,116            bodyA,117            bodyB,118            bodyC;119        120        for (row = 0; row < rows; row++) {121            for (col = 1; col < columns; col++) {122                bodyA = bodies[(col - 1) + (row * columns)];123                bodyB = bodies[col + (row * columns)];124                Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyA, bodyB: bodyB }, options)));125            }126            if (row > 0) {127                for (col = 0; col < columns; col++) {128                    bodyA = bodies[col + ((row - 1) * columns)];129                    bodyB = bodies[col + (row * columns)];130                    Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyA, bodyB: bodyB }, options)));131                    if (crossBrace && col > 0) {132                        bodyC = bodies[(col - 1) + ((row - 1) * columns)];133                        Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyC, bodyB: bodyB }, options)));134                    }135                    if (crossBrace && col < columns - 1) {136                        bodyC = bodies[(col + 1) + ((row - 1) * columns)];137                        Composite.addConstraint(composite, Constraint.create(Common.extend({ bodyA: bodyC, bodyB: bodyB }, options)));138                    }139                }140            }141        }142        composite.label += ' Mesh';143        144        return composite;145    };146    147    /**148     * Create a new composite containing bodies created in the callback in a pyramid arrangement.149     * This function uses the body's bounds to prevent overlaps.150     * @method pyramid151     * @param {number} xx152     * @param {number} yy153     * @param {number} columns154     * @param {number} rows155     * @param {number} columnGap156     * @param {number} rowGap157     * @param {function} callback158     * @return {composite} A new composite containing objects created in the callback159     */160    Composites.pyramid = function(xx, yy, columns, rows, columnGap, rowGap, callback) {161        return Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y, column, row, lastBody, i) {162            var actualRows = Math.min(rows, Math.ceil(columns / 2)),163                lastBodyWidth = lastBody ? lastBody.bounds.max.x - lastBody.bounds.min.x : 0;164            165            if (row > actualRows)166                return;167            168            // reverse row order169            row = actualRows - row;170            171            var start = row,172                end = columns - 1 - row;173            if (column < start || column > end)174                return;175            176            // retroactively fix the first body's position, since width was unknown177            if (i === 1) {178                Body.translate(lastBody, { x: (column + (columns % 2 === 1 ? 1 : -1)) * lastBodyWidth, y: 0 });179            }180            var xOffset = lastBody ? column * lastBodyWidth : 0;181            182            return callback(xx + xOffset + column * columnGap, y, column, row, lastBody, i);183        });184    };185    /**186     * Creates a composite with a Newton's Cradle setup of bodies and constraints.187     * @method newtonsCradle188     * @param {number} xx189     * @param {number} yy190     * @param {number} number191     * @param {number} size192     * @param {number} length193     * @return {composite} A new composite newtonsCradle body194     */195    Composites.newtonsCradle = function(xx, yy, number, size, length) {196        var newtonsCradle = Composite.create({ label: 'Newtons Cradle' });197        for (var i = 0; i < number; i++) {198            var separation = 1.9,199                circle = Bodies.circle(xx + i * (size * separation), yy + length, size, 200                    { inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0.0001, slop: 1 }),201                constraint = Constraint.create({ pointA: { x: xx + i * (size * separation), y: yy }, bodyB: circle });202            Composite.addBody(newtonsCradle, circle);203            Composite.addConstraint(newtonsCradle, constraint);204        }205        return newtonsCradle;206    };207    208    /**209     * Creates a composite with simple car setup of bodies and constraints.210     * @method car211     * @param {number} xx212     * @param {number} yy213     * @param {number} width214     * @param {number} height215     * @param {number} wheelSize216     * @return {composite} A new composite car body217     */218    Composites.car = function(xx, yy, width, height, wheelSize) {219        var group = Body.nextGroup(true),220            wheelBase = 20,221            wheelAOffset = -width * 0.5 + wheelBase,222            wheelBOffset = width * 0.5 - wheelBase,223            wheelYOffset = 0;224    225        var car = Composite.create({ label: 'Car' }),226            body = Bodies.rectangle(xx, yy, width, height, { 227                collisionFilter: {228                    group: group229                },230                chamfer: {231                    radius: height * 0.5232                },233                density: 0.0002234            });235    236        var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, { 237            collisionFilter: {238                group: group239            },240            friction: 0.8241        });242                    243        var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, { 244            collisionFilter: {245                group: group246            },247            friction: 0.8248        });249                    250        var axelA = Constraint.create({251            bodyB: body,252            pointB: { x: wheelAOffset, y: wheelYOffset },253            bodyA: wheelA,254            stiffness: 1,255            length: 0256        });257                        258        var axelB = Constraint.create({259            bodyB: body,260            pointB: { x: wheelBOffset, y: wheelYOffset },261            bodyA: wheelB,262            stiffness: 1,263            length: 0264        });265        266        Composite.addBody(car, body);267        Composite.addBody(car, wheelA);268        Composite.addBody(car, wheelB);269        Composite.addConstraint(car, axelA);270        Composite.addConstraint(car, axelB);271        return car;272    };273    /**274     * Creates a simple soft body like object.275     * @method softBody276     * @param {number} xx277     * @param {number} yy278     * @param {number} columns279     * @param {number} rows280     * @param {number} columnGap281     * @param {number} rowGap282     * @param {boolean} crossBrace283     * @param {number} particleRadius284     * @param {} particleOptions285     * @param {} constraintOptions286     * @return {composite} A new composite softBody287     */288    Composites.softBody = function(xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {289        particleOptions = Common.extend({ inertia: Infinity }, particleOptions);290        constraintOptions = Common.extend({ stiffness: 0.2, render: { type: 'line', anchors: false } }, constraintOptions);291        var softBody = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y) {292            return Bodies.circle(x, y, particleRadius, particleOptions);293        });294        Composites.mesh(softBody, columns, rows, crossBrace, constraintOptions);295        softBody.label = 'Soft Body';296        return softBody;297    };...default.js
Source:default.js  
1var express = require('express');2var router = express.Router();3var Default = require("../models/default.js");4router.route('/')5    .get(function(req, res){6        Default.find(function(err, defaults){7            if(err){console.log(err);}8            res.send(defaults);9        });10    })11    /*  the following post route will only ever be used once.12     *  it is there to provide the initial values for the calculator13     *  which will be generated everytime this application is14     *  loaded fresh on a new server.15     */16    .post(function(req, res){17        var defaults = new Default({18            monthlyRentTenantDef:req.body.monthlyRentTenantDef,19            monthlyRentTenantMin:req.body.monthlyRentTenantMin,20            monthlyRentTenantMax:req.body.monthlyRentTenantMax,21            monthlyRentPersonalDef:req.body.monthlyRentPersonalDef,22            monthlyRentPersonalMin:req.body.monthlyRentPersonalMin,23            monthlyRentPersonalMax:req.body.monthlyRentPersonalMax,24            targetPriceDef:req.body.targetPriceDef,25            targetPriceMin:req.body.targetPriceMin,26            targetPriceMax:req.body.targetPriceMax,27            downPaymentPercentageDef:req.body.downPaymentPercentageDef,28            downPaymentPercentageMin:req.body.downPaymentPercentageMin,29            downPaymentPercentageMax:req.body.downPaymentPercentageMax,30            mortgageRateDef:req.body.mortgageRateDef,31            mortgageRateMin:req.body.mortgageRateMin,32            mortgageRateMax:req.body.mortgageRateMax,33            yearsAmmoritizedDef:req.body.yearsAmmoritizedDef,34            yearsAmmoritizedMin:req.body.yearsAmmoritizedMin,35            yearsAmmoritizedMax:req.body.yearsAmmoritizedMax,36            incomeDef:req.body.incomeDef,37            incomeMin:req.body.incomeMin,38            incomeMax:req.body.incomeMax,39            mortgageYearsDef:req.body.mortgageYearsDef,40            mortgageYearsMin:req.body.mortgageYearsMin,41            mortgageYearsMax:req.body.mortgageYearsMax,42            vacancyDef:req.body.vacancyDef,43            vacancyMin:req.body.vacancyMin,44            vacancyMax:req.body.vacancyMax,45            propertyTaxDef:req.body.propertyTaxDef,46            propertyTaxMin:req.body.propertyTaxMin,47            propertyTaxMax:req.body.propertyTaxMax,48            assocDuesDef:req.body.assocDuesDef,49            assocDuesMin:req.body.assocDuesMin,50            assocDuesMax:req.body.assocDuesMax,51            managementDef:req.body.managementDef,52            managementMin:req.body.managementMin,53            managementMax:req.body.managementMax,54            miscDef:req.body.miscDef,55            miscMin:req.body.miscMin,56            miscMax:req.body.miscMax,57            insuranceAnnualDef:req.body.insuranceAnnualDef,58            insuranceAnnualMin:req.body.insuranceAnnualMin,59            insuranceAnnualMax:req.body.insuranceAnnualMax,60            utilsDef:req.body.utilsDef,61            utilsMin:req.body.utilsMin,62            utilsMax:req.body.utilsMax,63            legalAccountingDef:req.body.legalAccountingDef,64            legalAccountingMin:req.body.legalAccountingMin,65            legalAccountingMax:req.body.legalAccountingMax,66            taxBracketDef:req.body.taxBracketDef,67            taxBracketMin:req.body.taxBracketMin,68            taxBracketMax:req.body.taxBracketMax,69            repairValueDef:req.body.repairValueDef,70            repairValueMin:req.body.repairValueMin,71            repairValueMax:req.body.repairValueMax,72            yearsDef:req.body.yearsDef,73            yearsMin:req.body.yearsMin,74            yearsMax:req.body.yearsMax,75            zipCode: req.body.zipCode,76            renterInsuranceDef: req.body.renterInsuranceDef,77            renterInsuranceMin: req.body.renterInsuranceMin,78            renterInsuranceMax:req.body.renterInsuranceMax,79            appreciationRateDef:req.body.appreciationRateDef,80            appreciationRateMin:req.body.appreciationRateMin,81            appreciationRateMax:req.body.appreciationRateMax,82            appreciationRateHomeDef:req.body.appreciationRateHomeDef,83            appreciationRateHomeMin:req.body.appreciationRateHomeMin,84            appreciationRateHomeMax:req.body.appreciationRateHomeMax,85            duplexBuyDef:req.body.duplexBuyDef,86            duplexBuyMin:req.body.duplexBuyMin,87            duplexBuyMax:req.body.duplexBuyMax88        });89        defaults.save(function(err, defaults){90            if(err) console.log(err);91            res.send(defaults);92        });93    });94    router.route('/:id').put(function(req, res){95        Default.findById(req.body._id, function(err, defaults){96            if(err) res.send(err);97            console.log(req.body);98            defaults.monthlyRentTenantDef = req.body.monthlyRentTenantDef;99            defaults.monthlyRentTenantMin = req.body.monthlyRentTenantMin;100            defaults.monthlyRentTenantMax = req.body.monthlyRentTenantMax;101            defaults.monthlyRentPersonalDef = req.body.monthlyRentPersonalDef;102            defaults.monthlyRentPersonalMin = req.body.monthlyRentPersonalMin;103            defaults.monthlyRentPersonalMax = req.body.monthlyRentPersonalMax;104            defaults.targetPriceDef = req.body.targetPriceDef;105            defaults.targetPriceMin = req.body.targetPriceMin;106            defaults.targetPriceMax = req.body.targetPriceMax;107            defaults.downPaymentPercentageDef = req.body.downPaymentPercentageDef;108            defaults.downPaymentPercentageMin = req.body.downPaymentPercentageMin;109            defaults.downPaymentPercentageMax = req.body.downPaymentPercentageMax;110            defaults.mortgageRateDef = req.body.mortgageRateDef;111            defaults.mortgageRateMin = req.body.mortgageRateMin;112            defaults.mortgageRateMax = req.body.mortgageRateMax;113            defaults.yearsAmmoritizedDef = req.body.yearsAmmoritizedDef;114            defaults.yearsAmmoritizedMin = req.body.yearsAmmoritizedMin;115            defaults.yearsAmmoritizedMax = req.body.yearsAmmoritizedMax;116            defaults.incomeDef = req.body.incomeDef;117            defaults.incomeMin = req.body.incomeMin;118            defaults.incomeMax = req.body.incomeMax;119            defaults.mortgageYearsDef = req.body.mortgageYearsDef;120            defaults.mortgageYearsMin = req.body.mortgageYearsMin;121            defaults.mortgageYearsMax = req.body.mortgageYearsMax;122            defaults.vacancyDef = req.body.vacancyDef;123            defaults.vacancyMin = req.body.vacancyMin;124            defaults.vacancyMax = req.body.vacancyMax;125            defaults.propertyTaxDef = req.body.propertyTaxDef;126            defaults.propertyTaxMin = req.body.propertyTaxMin;127            defaults.propertyTaxMax = req.body.propertyTaxMax;128            defaults.assocDuesDef = req.body.assocDuesDef;129            defaults.assocDuesMin = req.body.assocDuesMin;130            defaults.assocDuesMax = req.body.assocDuesMax;131            defaults.managementDef = req.body.managementDef;132            defaults.managementMin = req.body.managementMin;133            defaults.managementMax = req.body.managementMax;134            defaults.miscDef = req.body.miscDef;135            defaults.miscMin = req.body.miscMin;136            defaults.miscMax = req.body.miscMax;137            defaults.insuranceAnnualDef = req.body.insuranceAnnualDef;138            defaults.insuranceAnnualMin = req.body.insuranceAnnualMin;139            defaults.insuranceAnnualMax = req.body.insuranceAnnualMax;140            defaults.utilsDef = req.body.utilsDef;141            defaults.utilsMin = req.body.utilsMin;142            defaults.utilsMax = req.body.utilsMax;143            defaults.legalAccountingDef = req.body.legalAccountingDef;144            defaults.legalAccountingMin = req.body.legalAccountingMin;145            defaults.legalAccountingMax = req.body.legalAccountingMax;146            defaults.taxBracketDef = req.body.taxBracketDef;147            defaults.taxBracketMin = req.body.taxBracketMin;148            defaults.taxBracketMax = req.body.taxBracketMax;149            defaults.repairValueDef = req.body.repairValueDef;150            defaults.repairValueMin = req.body.repairValueMin;151            defaults.repairValueMax = req.body.repairValueMax;152            defaults.yearsDef = req.body.yearsDef;153            defaults.yearsMin = req.body.yearsMin;154            defaults.yearsMax = req.body.yearsMax;155            defaults.zipCode = req.body.zipCode;156            defaults.renterInsuranceDef= req.body.renterInsuranceDef;157            defaults.renterInsuranceMin =req.body.renterInsuranceMin;158            defaults.renterInsuranceMax =req.body.renterInsuranceMax;159            defaults.appreciationRateDef=req.body.appreciationRateDef;160            defaults.appreciationRateMin=req.body.appreciationRateMin;161            defaults.appreciationRateMax=req.body.appreciationRateMax;162            defaults.appreciationRateHomeDef=req.body.appreciationRateHomeDef;163            defaults.appreciationRateHomeMin=req.body.appreciationRateHomeMin;164            defaults.appreciationRateHomeMax=req.body.appreciationRateHomeMax;165            defaults.duplexBuyDef=req.body.duplexBuyDef;166            defaults.duplexBuyMin=req.body.duplexBuyMin;167            defaults.duplexBuyMax=req.body.duplexBuyMax;168            defaults.save(function(err){169                if(err) res.send(err);170                res.json({message: 'defaults have been updated!'});171            });172        });173    });...worker.js
Source:worker.js  
1/**2 * Web worker containing a JigLibJS rigid-body physics system.3 *4 * @author xeolabs / http://xeolabs.com5 *6 * This worker accepts various commands to configure the system, add or7 * remove bodies, and integrate (which means run the system for one frame).8 *9 * After each integration, this worker posts back an array buffer containing10 * an updated position and direction for each body.11 *12 *13 * Input Commands14 * --------------------------------------------------------------------------15 *16 * Configure the system:17 * {18 *      cmd: "setConfigs",19 *      //..configs20 * }21 *22 * Create a body:23 * {24 *      cmd: "createBody",25 *      bodyId: Number,26 *      bodyCfg: {27 *          shape: "plane" | "box" | "sphere",28 *          movable: true | false,29 *          pos: [Number, Number, Number],30 *          mass: Number,31 *          restitution: Number,32 *          friction: Number,33 *          velocity: [Number, Number, Number]34 *      }35 *  }36 *37 * Remove a body:38 * {39 *      cmd: "removeBody",40 *      bodyId: Number41 * }42 *43 * Update a body:44 * {45 *      cmd: "updateBody",46 *      bodyId: Number,47 *      bodyCfg: {48 *          movable: true | false,49 *          pos: [Number, Number, Number],50 *          mass: Number,51 *          restitution: Number,52 *          friction: Number,53 *          velocity: [Number, Number, Number]54 *      }55 * }56 *57 * Integrate the phsycis system:58 * {59 *      cmd: "integrate"60 * }61 *62 *63 * For efficiency, the physics system manages bodies in an array. The "bodyId"64 * parameter on the "createBody" command is the index for that body in the array.65 *66 * The "removeBody" command will delete a body from the array, leaving a hole.67 *68 * The worker can handle holes in the array OK, but in order to keep the array69 * from getting too sparse, it's the reponsibility of the worker client to make70 * its next "createBody" command specify a "bodyId" that indexes that hole, to plug71 * the gap with the next new body.72 *73 *74 * Output Buffer75 * --------------------------------------------------------------------------76 *77 * The output buffer contains a 20-element portion for each physics body, each of78 * which contains the body ID, a new position, and a 16-element rotation matrix:79 *80 * [81 *      bodyId, xPos, yPos, zPos, mat0, ... mat15,82 *      bodyId, xPos, yPos, zPos, mat0, ... mat15,83 *      ...84 * ]85 *86 */87importScripts("jiglib.all.min.js");88var bodies = [];89var numBodies = 0;90// Array in which this worker posts back91// an updated position and direction for each body92var output;93// Physics engine system94var system = jigLib.PhysicsSystem.getInstance();95// Set initial default configuration for physics system96setConfigs();97/** Configures JigLibJS98 */99function setConfigs(params) {100    params = params || {};101    system.setGravity(params.gravity || [0, -9.8, 0, 0]); //-120102    system.setSolverType(params.solver || 'ACCUMULATED'); //FAST, NORMAL, ACCUMULATED103}104// System starts immediately105var then = (new Date()).getTime();106// Handle command from worker owner107addEventListener("message",108    function (e) {109        var data = e.data;110        switch (data.cmd) {111            // Configure the physics system112            case "setConfigs":113                setConfigs(data.configs);114                break;115            // Create a physics body116            case "createBody":117                var bodyId = data.bodyId;118                var bodyCfg = data.bodyCfg;119                var shape = bodyCfg.shape;120                var body;121                switch (shape) {122                    case "plane":123                        body = new jigLib.JPlane(null, bodyCfg.dir || [0, 1, 0]);124                        break;125                    case "box":126                        body = new jigLib.JBox(null, bodyCfg.width || 1.0, bodyCfg.depth || 1.0, bodyCfg.height || 1.0);127                        break;128                    case "sphere":129                        body = new jigLib.JSphere(null, bodyCfg.radius || 1.0);130                        break;131                    default:132                        // Unsupported body type133                        return;134                }135                bodies[bodyId] = {136                    body:body,137                    spherical:shape == "sphere"138                };139                system.addBody(body);140                if (bodyCfg.movable != undefined) {141                    body.set_movable(!!bodyCfg.movable);142                }143                if (bodyCfg.pos) {144                    body.moveTo(bodyCfg.pos);145                }146                if (bodyCfg.mass != undefined) {147                    body.set_mass(bodyCfg.mass);148                }149                if (bodyCfg.restitution != undefined) {150                    body.set_restitution(bodyCfg.restitution);151                }152                if (bodyCfg.friction != undefined) {153                    body.set_friction(bodyCfg.friction);154                }155                if (bodyCfg.velocity != undefined) {156                    body.setVelocity(bodyCfg.velocity);157                }158                numBodies++;159                break;160            // Update a physics body161            case "updateBody":162                var bodyId = data.bodyId;163                var body = bodies[bodyId].body;164                if (!body) {165                    return;166                }167                var bodyCfg = data.bodyCfg;168                if (bodyCfg.movable != undefined) {169                    body.set_movable(!!bodyCfg.movable);170                }171                if (bodyCfg.pos) {172                    body.moveTo(bodyCfg.pos);173                }174                if (bodyCfg.mass != undefined) {175                    body.set_mass(bodyCfg.mass);176                }177                if (bodyCfg.restitution != undefined) {178                    body.set_restitution(bodyCfg.restitution);179                }180                if (bodyCfg.friction != undefined) {181                    body.set_friction(bodyCfg.friction);182                }183                if (bodyCfg.velocity != undefined) {184                    body.setVelocity(bodyCfg.velocity);185                }186                break;187            // Remove a physics body188            case "removeBody":189                var body = bodies[data.bodyId];190                if (!body) {191                    return;192                }193                bodies[data.bodyId] = null;194                system.removeBody(body);195                numBodies--;196                break;197            // Integrate the physics system and post back the body updates198            case "integrate":199                var output = new Float32Array(data.buffer);200                var now = (new Date()).getTime();201                //       if (numBodies > 0) { // Only integrate and post if there are bodies202                var secs = (now - then) / 1000;203                var item;204                var body;205                var spherical;206                var state;207                var pos;208                var dir;209                var ibuf = 0;210                system.integrate(secs);211                for (var bodyId = 0, ibody = 0; ibody < numBodies; bodyId++) {212                    item = bodies[bodyId];213                    if (!item) { // Deleted214                        continue;215                    }216                    body = item.body;217                    spherical = item.spherical;218                    state = body.get_currentState();219                    // Body ID220                    output[ibuf++] = bodyId;221                    // New position222                    pos = state.position;223                    output[ibuf++] = pos[0];224                    output[ibuf++] = pos[1];225                    output[ibuf++] = pos[2];226                    if (spherical) {227                        // No rotation necessary for spheres228                        ibuf += 16;229                    } else {230                        // New rotation matrix231                        dir = state.get_orientation().glmatrix;232                        output[ibuf++] = dir[0];233                        output[ibuf++] = dir[1];234                        output[ibuf++] = dir[2];235                        output[ibuf++] = dir[3];236                        output[ibuf++] = dir[4];237                        output[ibuf++] = dir[5];238                        output[ibuf++] = dir[6];239                        output[ibuf++] = dir[7];240                        output[ibuf++] = dir[8];241                        output[ibuf++] = dir[9];242                        output[ibuf++] = dir[10];243                        output[ibuf++] = dir[11];244                        output[ibuf++] = dir[12];245                        output[ibuf++] = dir[13];246                        output[ibuf++] = dir[14];247                        output[ibuf++] = dir[15];248                    }249                    ibody++; // Next body;250                }251                // Post the output252                var response = {253                    buffer:output.buffer,254                    lenOutput:ibuf - 20255                };256                self.postMessage(response, [response.buffer]);257                then = now;258                break;259            default:260                break;261        }...racket.js
Source:racket.js  
1/**2 * racket : Not optimally desgiend, but to assist in some gaming utility and 3 * physics for animation.4 * 5 * The racket namespace currently contains the two libraries:6 * 7 * 1. physikz: supports cheap physics and collision detection.8 * 2. num: a lib of utility methods to work with numbers.9 * 10 * dependencies: See the bower.json file for current dependency versions, and 11 * ensure you add dependencies to your index.html file, as in:12 * 13 * <script src="bower_components/lodash/lodash.min.js"></script>14 *15 */16(function (window) {17    window.opspark = window.opspark || {};18    19    function sortNumbersAscending(a, b) { return a - b; }20    21    function sortNumbersDecending(a, b) { return b - a; }22    23    function randomIntBetween(min, max) { 24        return Math.floor(Math.random() * (max - min + 1) + min);25    }26    27    // radians = degrees * Math.PI / 180 //28    function degreesToRadians(degrees) {29        return degrees * Math.PI / 180;30    }31    32    // degrees = radians * 180 / Math.PI //33    function radiansToDegrees(radians) {34        return radians * 180 / Math.PI;35    }36    37    function getDistance(pointOne, pointTwo) {38        var distanceX = pointTwo.x - pointOne.x;39        var distanceY = pointTwo.y - pointOne.y;40        return Math.sqrt(distanceX * distanceX + distanceY * distanceY);41    }42    43    function getDistanceProperties(bodyA, bodyB) {44        var distanceX = bodyB.x - bodyA.x;45        var distanceY = bodyB.y - bodyA.y;46        return {47            bodyA: bodyA,48            bodyB: bodyB,49            distanceX: distanceX,50            distanceY: distanceY,51            distance: Math.sqrt(distanceX * distanceX + distanceY * distanceY)52        };53    }54    55    function hitTestRadial(distance, bodyA, bodyB) { 56        var radiusCombined = bodyA.radius + bodyB.radius;57        return {58            bodyA: bodyA,59            bodyB: bodyB,60            isHit: (distance < radiusCombined),61            radiusCombined: radiusCombined62        };63    }64    65    function getImpactProperties(bodyA, bodyB) {66        var combinedVolatility = bodyA.volatility + bodyB.volatility;67        var combinedDensity = bodyA.density * bodyB.density;68        return {69            bodyA: bodyA,70            bodyB: bodyB,71            combinedVolatility: combinedVolatility,72            combinedDensity: combinedDensity,73            impact: (combinedVolatility ? combinedVolatility * combinedDensity : combinedDensity)74        };75    }76    77    var racket = {78        physikz: {79            addRandomVelocity: function (body, area, multiplierX, multiplierY) {80                if (!body.integrity) { _.extend(body, this.makeBody()); }81                82                multiplierX = (multiplierX) ? multiplierX : .6;83                multiplierY = (multiplierY) ? multiplierY : .5;84                85                var tx = randomIntBetween(0, area.width);86                var ty = randomIntBetween(0, area.height);87                var dx = Math.abs(tx - body.x);88                var dy = Math.abs(ty - body.y);89                var radians = Math.atan2(dy, dx);90                body.rotation = radiansToDegrees(radians);91                92                var rotationalDirection = (Math.round(Math.random()) === 1) ? 1 : -1;93                body.rotationalVelocity = randomIntBetween(1, 3) * rotationalDirection;94                var forceX = Math.cos(radians) * (Math.random() * multiplierX);95                var forceY = Math.sin(radians) * (Math.random() * multiplierY);96                97                body.velocityX = (tx > body.x) ? forceX : -forceX;98                body.velocityY = (ty > body.y) ? forceY : -forceY;99            },100            101            updatePosition: function (body) {102                body.x += body.velocityX;103                body.y += body.velocityY;104                body.rotation += body.rotationalVelocity;105            },106            107            updateRadialPositionInArea: function (body, area) {108                var radius = body.radius;109                var w  = area.width + radius * 2;110                var h = area.height + radius * 2;111                112                body.x = (body.x + radius + body.velocityX + w) % w - radius;113                body.y = (body.y + radius + body.velocityY + h) % h - radius;114                body.rotation += body.rotationalVelocity;115            },116            117            updateRadialPositionAndReboundInArea: function (body, area) {118                var radius = body.radius;119                var top = 0;120                var left = 0;121                var right = area.width;122                var bottom = area.height;123                124                body.x += body.velocityX;125                body.y += body.velocityY;126                body.rotation += body.rotationalVelocity;127                128                if (body.x + radius > right) {129                    body.x = right - radius;130                    body.velocityX *= -1;131                    132                } else if (body.x - radius < left) {133                    body.x = left + radius;134                    body.velocityX *= -1;135                }136                137                if (body.y + radius > bottom) {138                    body.y = bottom - radius;139                    body.velocityY *= -1;140                } else if (body.y - radius < top) {141                    body.y = top + radius;142                    body.velocityY *= -1;143                }144            },145            146            /*147             * getDistance: Using the Pythagorean Theorem, returns the 148             *      distance between two points.149             *150             * @return A Number representing the distance between two points.151             */152            getDistance: getDistance,153            154            /*155             * getDistanceProperties: Using the Pythagorean Theorem, returns an 156             *      distanceobject with properties distance, distanceX, and distanceY.157             *158             * @return Object  An object with properties pointOne, pointTwo, 159             *      distance, distanceX, and distanceY.160             */161            getDistanceProperties: getDistanceProperties,162            163            /*164             * Takes to bodies, returns an object with their combinedVolatility, 165             *      combinedDensity, and impact.166             */167            getImpactProperties: getImpactProperties,168            169            /*170             * hitTestRadial: Expects the distance betwo bodies with a radius property. Returns 171             *      an object with the result of the radial hit test, with the 172             *      property isHit being true if the distance between the x/y of 173             *      the radial shapes is less than the sum of their two radius.174             *175             * @return Object176             */177            hitTestRadial: hitTestRadial,178            179            /*180             * Takes an Array of bodies to manage as the space, a hitTest 181             *      function to preform between each body in the space, and a 182             *      handleCollision function designed to respond to collision. 183             */184            updateSpace: function (space, hitTest, handleCollision) {185                for(var i = space.length - 1; i > 0; i--) {186                    var bodyA = space[i];187                    for(var j = i - 1; j > -1; j--) {188                        var bodyB = space[j];189                        var distanceProperties = getDistanceProperties(bodyA, bodyB);190                        var hitResult = hitTest(distanceProperties.distance, bodyA, bodyB);191                        if(hitResult.isHit) {192                            handleCollision(distanceProperties, hitResult, getImpactProperties(bodyA, bodyB));193                        }194                    }195                }196            },197            198            makeBody: function (type, velocityX, velocityY, rotationalVelocity, integrity, density, volatility) {199                return {200                    type: type || 'undefined',201                    velocityX: velocityX || 0,202                    velocityY: velocityY || 0,203                    rotationalVelocity: rotationalVelocity || 0,204                    integrity: integrity || 1,205                    density: density || 1,206                    volatility: volatility || 0,207                    208                    handleCollision: function (impact, body) {209                        // template method //210                    }211                };212            },213            214            degreesToRadians: degreesToRadians,215            radiansToDegrees: radiansToDegrees216        },217        218        num: {219            randomIntBetween: randomIntBetween,220            sortNumbersAscending: sortNumbersAscending,221            sortNumbersDecending: sortNumbersDecending,222            degreesToRadians: degreesToRadians,223            radiansToDegrees: radiansToDegrees224        }225    };226    window.opspark.racket = racket;...Sleeping.js
Source:Sleeping.js  
1/**2* The `Matter.Sleeping` module contains methods to manage the sleeping state of bodies.3*4* @class Sleeping5*/6var Sleeping = {};7module.exports = Sleeping;8var Events = require('./Events');9(function() {10    Sleeping._motionWakeThreshold = 0.18;11    Sleeping._motionSleepThreshold = 0.08;12    Sleeping._minBias = 0.9;13    /**14     * Puts bodies to sleep or wakes them up depending on their motion.15     * @method update16     * @param {body[]} bodies17     * @param {number} timeScale18     */19    Sleeping.update = function(bodies, timeScale) {20        var timeFactor = timeScale * timeScale * timeScale;21        // update bodies sleeping status22        for (var i = 0; i < bodies.length; i++) {23            var body = bodies[i],24                motion = body.speed * body.speed + body.angularSpeed * body.angularSpeed;25            // wake up bodies if they have a force applied26            if (body.force.x !== 0 || body.force.y !== 0) {27                Sleeping.set(body, false);28                continue;29            }30            var minMotion = Math.min(body.motion, motion),31                maxMotion = Math.max(body.motion, motion);32        33            // biased average motion estimation between frames34            body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion;35            36            if (body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeFactor) {37                body.sleepCounter += 1;38                39                if (body.sleepCounter >= body.sleepThreshold)40                    Sleeping.set(body, true);41            } else if (body.sleepCounter > 0) {42                body.sleepCounter -= 1;43            }44        }45    };46    /**47     * Given a set of colliding pairs, wakes the sleeping bodies involved.48     * @method afterCollisions49     * @param {pair[]} pairs50     * @param {number} timeScale51     */52    Sleeping.afterCollisions = function(pairs, timeScale) {53        var timeFactor = timeScale * timeScale * timeScale;54        // wake up bodies involved in collisions55        for (var i = 0; i < pairs.length; i++) {56            var pair = pairs[i];57            58            // don't wake inactive pairs59            if (!pair.isActive)60                continue;61            var collision = pair.collision,62                bodyA = collision.bodyA.parent, 63                bodyB = collision.bodyB.parent;64        65            // don't wake if at least one body is static66            if ((bodyA.isSleeping && bodyB.isSleeping) || bodyA.isStatic || bodyB.isStatic)67                continue;68        69            if (bodyA.isSleeping || bodyB.isSleeping) {70                var sleepingBody = (bodyA.isSleeping && !bodyA.isStatic) ? bodyA : bodyB,71                    movingBody = sleepingBody === bodyA ? bodyB : bodyA;72                if (!sleepingBody.isStatic && movingBody.motion > Sleeping._motionWakeThreshold * timeFactor) {73                    Sleeping.set(sleepingBody, false);74                }75            }76        }77    };78  79    /**80     * Set a body as sleeping or awake.81     * @method set82     * @param {body} body83     * @param {boolean} isSleeping84     */85    Sleeping.set = function(body, isSleeping) {86        var wasSleeping = body.isSleeping;87        if (isSleeping) {88            body.isSleeping = true;89            body.sleepCounter = body.sleepThreshold;90            body.positionImpulse.x = 0;91            body.positionImpulse.y = 0;92            body.positionPrev.x = body.position.x;93            body.positionPrev.y = body.position.y;94            body.anglePrev = body.angle;95            body.speed = 0;96            body.angularSpeed = 0;97            body.motion = 0;98            if (!wasSleeping) {99                Events.trigger(body, 'sleepStart');100            }101        } else {102            body.isSleeping = false;103            body.sleepCounter = 0;104            if (wasSleeping) {105                Events.trigger(body, 'sleepEnd');106            }107        }108    };...filter.js
Source:filter.js  
1const fs = require("fs");2let testJobs3let filteredJobs = []4let removedJobs = []5const loadJobs = async () => {6  await fs.readFile('./jobs/allScraped.js', async (err, data) => {7    if (err) throw err;8    testJobs = await JSON.parse(data)9  });10}11// checkTitle = (title) => {12//   return title.includes('Sr')13//     || title.includes('Senior')14//     || title.includes('senior')15//     || title.includes('Lead')16//     ? true : false17// }18checkBody = (body) => {19  return body.includes('Sr')20    || body.includes('Senior')21    || body.includes('senior')22    || body.includes('4+')23    || body.includes('5+')24    || body.includes('6+')25    || body.includes('7+')26    || body.includes('8+')27    || body.includes('9+')28    || body.includes('10+')29    || body.includes('11+')30    || body.includes('12+')31    || body.includes('13+')32    || body.includes('14+')33    || body.includes('15+')34    || body.includes('4 + years')35    || body.includes('5 + years')36    || body.includes('6 + years')37    || body.includes('7 + years')38    || body.includes('8 + years')39    || body.includes('9 + years')40    || body.includes('10 + years')41    || body.includes('11 + years')42    || body.includes('12 + years')43    || body.includes('13 + years')44    || body.includes('14 + years')45    || body.includes('15 + years')46    || body.includes('4 years')47    || body.includes('5 years')48    || body.includes('6 years')49    || body.includes('7 years')50    || body.includes('8 years')51    || body.includes('9 years')52    || body.includes('10 years')53    || body.includes('11 years')54    || body.includes('12 years')55    || body.includes('13 years')56    || body.includes('14 years')57    || body.includes('15 years')58    || body.includes('4 plus years')59    || body.includes('5 plus years')60    || body.includes('6 plus years')61    || body.includes('7 plus years')62    || body.includes('8 plus years')63    || body.includes('9 plus years')64    || body.includes('10 plus years')65    || body.includes('11 plus years')66    || body.includes('12 plus years')67    || body.includes('13 plus years')68    || body.includes('14 plus years')69    || body.includes('15 plus years')70    || body.includes('our client')71    || body.includes('Our client')72    || body.includes('Our Client')73    || body.includes('my client')74    || body.includes('My client')75    || body.includes('My Client')76    ? true : false77}78// checkCompany = (company) => {79//   return company.includes('Jobs @')80//     || company.includes('Andiamo')81//     || company.includes('CyberCoders')82//     || company.includes('Jobspring')83//     || company.includes('ClearedJobs')84//     ? true : false85// }86check = () => {87  for (job of testJobs) {88    // checkCompany(job.company) || checkTitle(job.title) || checkBody(job.body) ? removedJobs.push(job) : filteredJobs.push(job)89    checkBody(job.body) ? removedJobs.push(job) : filteredJobs.push(job)90  }91  saveJobs()92  // console.log(filteredJobs)93}94// writeJobs = () => {95//   const parent = document.querySelector('#jobs')96//   const el = document.createElement('li')97//   filteredJobs.forEach(job => {98//     el.innerHTML=job99//     parent.appenChild(el)100//   })101// }102const saveJobs = async() => {103  await fs.writeFile(`./jobs/allFilteredJobs.js`, JSON.stringify(filteredJobs), function (err) {104    if (err) {105      console.log(err);106    }107    else {108      console.log("Output saved to /allFilteredJobs.js.");109    }110  });111}112const runFilter = async () => {113  await loadJobs()114  setTimeout(check, 2000)115  // console.log(testJobs)116}...Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.click('text=Sign in');7  await page.fill('#identifierId', 'your_email');8  await page.click('text=Next');9  await page.fill('input[type="password"]', 'your_password');10  await page.click('text=Next');11  await page.click('text=Images');12  await page.click('text=Search by image');13  await page.setInputFiles('input[type="file"]', './test.png');14  await page.waitForTimeout(5000);15  await page.click('text=Search by image');16  await page.waitForTimeout(5000);17  await page.screenshot({ path: 'test.png' });18  await browser.close();19})();20{21  "scripts": {22  },23  "dependencies": {24  }25}Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.click('input[type="text"]');7  await page.keyboard.type('Hello World!');8  await page.screenshot({ path: 'screenshot.png' });9  await browser.close();10})();11const { chromium } = require('playwright');12(async () => {13  const browser = await chromium.launch();14})();15const { chromium } = require('playwright');16(async () => {17  const browser = await chromium.launch({18  });19})();20const { chromium } = require('playwright');21(async () => {22  const browser = await chromium.launch();23  const context = await browser.newContext();24})();25const { chromium } = require('playwright');26(async () => {27  const browser = await chromium.launch();28  const context = await browser.newContext();29  const page = await context.newPage();30})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.evaluate(() => {7    const form = document.createElement('form');8    form.method = 'post';9    form.target = '_blank';10    const input = document.createElement('input');11    input.type = 'text';12    input.name = 'foo';13    input.value = 'bar';14    form.appendChild(input);15    document.body.appendChild(form);16    form.submit();17  });18  await browser.close();19})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.click('text=Get started');7  await page.fill('input[name="q"]', 'hello world');8  await page.click('text=Submit');9  await page.waitForTimeout(3000);10  await browser.close();11})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch({ headless: false });4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.fill('input[aria-label="Search"]', 'playwright');7  await page.keyboard.press('Enter');8  await page.waitForSelector('text=Playwright');9  await page.click('text=Playwright');10  const body = await page.$('body');11  console.log(await body.innerHTML());12  await page.close();13  await context.close();14  await browser.close();15})();16const { chromium } = require('playwright');17(async () => {18  const browser = await chromium.launch({ headless: false });19  const context = await browser.newContext();20  const page = await context.newPage();21  await page.fill('input[aria-label="Search"]', 'playwright');22  await page.keyboard.press('Enter');23  await page.waitForSelector('text=Playwright');24  await page.click('text=Playwright');25  const body = await page.$('body');26  console.log(await body.innerHTML());27  await page.close();28  await context.close();29  await browser.close();30})();31const { chromium } = require('playwright');32(async () => {33  const browser = await chromium.launch({ headless: false });34  const context = await browser.newContext();35  const page = await context.newPage();36  await page.fill('input[aria-label="Search"]', 'playwright');37  await page.keyboard.press('Enter');38  await page.waitForSelector('text=Playwright');39  await page.click('text=Playwright');40  const body = await page.$('body');41  console.log(await body.innerHTML());42  await page.close();43  await context.close();44  await browser.close();45})();46const { chromium } = requireUsing AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  const body = await page.evaluateHandle(() => document.body);7  await body.evaluate(body => body.style.backgroundColor = 'red');8  await page.screenshot({ path: `example.png` });9  await browser.close();10})();11#### 3.1.1. browser.newContext([options])Using AI Code Generation
1const );cromium } = rquire('plywright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  const boy = await page.evauateHandle(() => document.body);7  await body.evaluate(body => body.style.backgroundColor = 'red');8  await page.screenshot({ path: `example.png` });9  await browser.close();10})();11#### 3.1.1. browser.newContext([options])Using AI Code Generation
1const { chromium  = require('playwright'2(async () => {3  const browser = await chromium.launch({ headless: false });4  const context = await browser.newContext();5  const page = await context.newPage();6  await page.click('text=Get started');7  await page.fill('input[name="q"]', 'hello world');8  await page.click('text=Submit');9  await page.waitForTimeout(3000);10  await browser.close();11})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch({ headless: false });4const { chromium } = require('playwright');5(async () => {6  const browser = await chromium.launch();7  const cot()xt = await browse;.ewContext();8  const page = await context.newPage();9 const response = await page.route('**/api/counter', route => {10    route.fulfill({11      body: JSON.stringify({ value: 42 }),12    });13  });14  await page.click('text=Counter');15  await page.waitForSelector('text=42');16  await browser.close();17})();Using AI Code Generation
1  const page = await context.newPage();2  await page.fill('input[title="Search"]', 'Playwright');3  await page.click('input[type="submit"]');4  await page.waitForSelector('text="Playwright - Google Search"');5  await page.click('text="Playwright - Google Search"');6  await page.waitForNavigation();7  const body = await page.$('body');8  const html = await body.innerHTML();9  console.log(html);10  await browser.close();11})();Using AI Code Generation
1const body = await page.$eval('body', body => body.innerHTML);2const body = await page.content();3const iPhone = playwright.devices['iPhone 6'];4await page.emulate(iPhone);5const iPad = playwright.devices['iPad landscape'];6await page.emulate(iPad);7await page.screenshot({ path: 'screenshot.png' });8await page.pdf({ path: 'page.pdf' });9await page.video().saveAs('video.mp4');10await page.tracing.start({ screenshots: true, snapshots: true });11await page.tracing.stop({ path: 'trace.zip' });12await page.tracing.start({ screenshots: true, snapshots: true });13await page.tracing.stop({ path: 'trace.zip' });14await page.type('#my-input', 'Hello World!');15await page.click('button');16await page.selectOption('#my-select', 'option');17await page.check('#my-checkbox');Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  const response = await page.route('**/api/counter', route => {7    route.fulfill({8      body: JSON.stringify({ value: 42 }),9    });10  });11  await page.click('text=Counter');12  await page.waitForSelector('text=42');13  await browser.close();14})();Using AI Code Generation
1const { chromium } = require('playwright');2(async () => {3  const browser = await chromium.launch();4  const context = await browser.newContext();5  const page = await context.newPage();6  const body = await response.body();7  console.log(body.toString());8  await browser.close();9})();Using AI Code Generation
1const body = await page.$eval('body', body => body.innerHTML);2const body = await page.content();3const iPhone = playwright.devices['iPhone 6'];4await page.emulate(iPhone);5const iPad = playwright.devices['iPad landscape'];6await page.emulate(iPad);7await page.screenshot({ path: 'screenshot.png' });8await page.pdf({ path: 'page.pdf' });9await page.video().saveAs('video.mp4');10await page.tracing.start({ screenshots: true, snapshots: true });11await page.tracing.stop({ path: 'trace.zip' });12await page.tracing.start({ screenshots: true, snapshots: true });13await page.tracing.stop({ path: 'trace.zip' });14await page.type('#my-input', 'Hello World!');15await page.click('button');16await page.selectOption('#my-select', 'option');17await page.check('#my-checkbox');LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!
