<commit_before>/**
* The `Matter.Resolver` module contains methods for resolving collision pairs.
*
* @class Resolver
*/

var Resolver = {};

module.exports = Resolver;

var Vertices = require('../geometry/Vertices');
var Vector = require('../geometry/Vector');
var Common = require('../core/Common');
var Bounds = require('../geometry/Bounds');

(function() {

    Resolver._restingThresh = 4;
    Resolver._restingThreshTangent = 6;
    Resolver._positionDampen = 0.9;
    Resolver._positionWarming = 0.8;
    Resolver._frictionNormalMultiplier = 5;

    /**
     * Prepare pairs for position solving.
     * @method preSolvePosition
     * @param {pair[]} pairs
     */
    Resolver.preSolvePosition = function(pairs) {
        var i,
            pair,
            activeCount;

        // find total contacts on each body
        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive)
                continue;

            activeCount = pair.activeContacts.length;
            pair.collision.parentA.totalContacts += activeCount;
            pair.collision.parentB.totalContacts += activeCount;
        }
    };

    /**
     * Find a solution for pair positions.
     * @method solvePosition
     * @param {pair[]} pairs
     * @param {number} timeScale
     */
    Resolver.solvePosition = function(pairs, timeScale) {
        var i,
            pair,
            collision,
            bodyA,
            bodyB,
            normal,
            bodyBtoA,
            contactShare,
            positionImpulse,
            contactCount = {},
            tempA = Vector._temp[0],
            tempB = Vector._temp[1],
            tempC = Vector._temp[2],
            tempD = Vector._temp[3];

        // find impulses required to resolve penetration
        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            collision = pair.collision;
            bodyA = collision.parentA;
            bodyB = collision.parentB;
            normal = collision.normal;

            // get current separation between body edges involved in collision
            bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA),
                                    Vector.add(bodyA.positionImpulse,
                                        Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD);

            pair.separation = Vector.dot(normal, bodyBtoA);
        }

        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive || pair.isSensor || pair.separation < 0)
                continue;

            collision = pair.collision;
            bodyA = collision.parentA;
            bodyB = collision.parentB;
            normal = collision.normal;
            positionImpulse = (pair.separation - pair.slop) * timeScale;

            if (bodyA.isStatic || bodyB.isStatic)
                positionImpulse *= 2;

            if (!(bodyA.isStatic || bodyA.isSleeping)) {
                contactShare = Resolver._positionDampen / bodyA.totalContacts;
                bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
                bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
            }

            if (!(bodyB.isStatic || bodyB.isSleeping)) {
                contactShare = Resolver._positionDampen / bodyB.totalContacts;
                bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
                bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
            }
        }
    };

    /**
     * Apply position resolution.
     * @method postSolvePosition
     * @param {body[]} bodies
     */
    Resolver.postSolvePosition = function(bodies) {
        for (var i = 0; i < bodies.length; i++) {
            var body = bodies[i];

            // reset contact count
            body.totalContacts = 0;

            if (body.positionImpulse.x !== 0 || body.positionImpulse.y !== 0) {
                // update body geometry
                for (var j = 0; j < body.parts.length; j++) {
                    var part = body.parts[j];
                    Vertices.translate(part.vertices, body.positionImpulse);
                    Bounds.update(part.bounds, part.vertices, body.velocity);
                    part.position.x += body.positionImpulse.x;
                    part.position.y += body.positionImpulse.y;
                }

                // move the body without changing velocity
                body.positionPrev.x += body.positionImpulse.x;
                body.positionPrev.y += body.positionImpulse.y;

                if (Vector.dot(body.positionImpulse, body.velocity) < 0) {
                    // reset cached impulse if the body has velocity along it
                    body.positionImpulse.x = 0;
                    body.positionImpulse.y = 0;
                } else {
                    // warm the next iteration
                    body.positionImpulse.x *= Resolver._positionWarming;
                    body.positionImpulse.y *= Resolver._positionWarming;
                }
            }
        }
    };

    /**
     * Prepare pairs for velocity solving.
     * @method preSolveVelocity
     * @param {pair[]} pairs
     */
    Resolver.preSolveVelocity = function(pairs) {
        var i,
            j,
            pair,
            contacts,
            collision,
            bodyA,
            bodyB,
            normal,
            tangent,
            contact,
            contactVertex,
            normalImpulse,
            tangentImpulse,
            offset,
            impulse = Vector._temp[0],
            tempA = Vector._temp[1];

        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            contacts = pair.activeContacts;
            collision = pair.collision;
            bodyA = collision.parentA;
            bodyB = collision.parentB;
            normal = collision.normal;
            tangent = collision.tangent;

            // resolve each contact
            for (j = 0; j < contacts.length; j++) {
                contact = contacts[j];
                contactVertex = contact.vertex;
                normalImpulse = contact.normalImpulse;
                tangentImpulse = contact.tangentImpulse;

                if (normalImpulse !== 0 || tangentImpulse !== 0) {
                    // total impulse from contact
                    impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
                    impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);

                    // apply impulse from contact
                    if (!(bodyA.isStatic || bodyA.isSleeping)) {
                        offset = Vector.sub(contactVertex, bodyA.position, tempA);
                        bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
                        bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
                        bodyA.anglePrev += Vector.cross(offset, impulse) * bodyA.inverseInertia;
                    }

                    if (!(bodyB.isStatic || bodyB.isSleeping)) {
                        offset = Vector.sub(contactVertex, bodyB.position, tempA);
                        bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
                        bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
                        bodyB.anglePrev -= Vector.cross(offset, impulse) * bodyB.inverseInertia;
                    }
                }
            }
        }
    };

    /**
     * Find a solution for pair velocities.
     * @method solveVelocity
     * @param {pair[]} pairs
     * @param {number} timeScale
     */
    Resolver.solveVelocity = function(pairs, timeScale) {
        var timeScaleSquared = timeScale * timeScale,
            impulse = Vector._temp[0],
            tempA = Vector._temp[1],
            tempB = Vector._temp[2],
            tempC = Vector._temp[3],
            tempD = Vector._temp[4],
            tempE = Vector._temp[5];

        for (var i = 0; i < pairs.length; i++) {
            var pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            var collision = pair.collision,
                bodyA = collision.parentA,
                bodyB = collision.parentB,
                normal = collision.normal,
                tangent = collision.tangent,
                contacts = pair.activeContacts,
                contactShare = 1 / contacts.length;

            // update body velocities
            bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x;
            bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y;
            bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x;
            bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y;
            bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
            bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;

            // resolve each contact
            for (var j = 0; j < contacts.length; j++) {
                var contact = contacts[j],
                    contactVertex = contact.vertex,
                    offsetA = Vector.sub(contactVertex, bodyA.position, tempA),
                    offsetB = Vector.sub(contactVertex, bodyB.position, tempB),
                    velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity), tempC),
                    velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity), tempD),
                    relativeVelocity = Vector.sub(velocityPointA, velocityPointB, tempE),
                    normalVelocity = Vector.dot(normal, relativeVelocity);

                var tangentVelocity = Vector.dot(tangent, relativeVelocity),
                    tangentSpeed = Math.abs(tangentVelocity),
                    tangentVelocityDirection = Common.sign(tangentVelocity);

                // raw impulses
                var normalImpulse = (1 + pair.restitution) * normalVelocity,
                    normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * Resolver._frictionNormalMultiplier;

                // coulomb friction
                var tangentImpulse = tangentVelocity,
                    maxFriction = Infinity;

                if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) {
                    maxFriction = tangentSpeed;
                    tangentImpulse = Common.clamp(
                        pair.friction * tangentVelocityDirection * timeScaleSquared,
                        -maxFriction, maxFriction
                    );
                }

                // modify impulses accounting for mass, inertia and offset
                var oAcN = Vector.cross(offsetA, normal),
                    oBcN = Vector.cross(offsetB, normal),
                    share = contactShare / (bodyA.inverseMass + bodyB.inverseMass + bodyA.inverseInertia * oAcN * oAcN  + bodyB.inverseInertia * oBcN * oBcN);

                normalImpulse *= share;
                tangentImpulse *= share;

                // handle high velocity and resting collisions separately
                if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) {
                    // high normal velocity so clear cached contact normal impulse
                    contact.normalImpulse = 0;
                } else {
                    // solve resting collision constraints using Erin Catto's method (GDC08)
                    // impulse constraint tends to 0
                    var contactNormalImpulse = contact.normalImpulse;
                    contact.normalImpulse = Math.min(contact.normalImpulse + normalImpulse, 0);
                    normalImpulse = contact.normalImpulse - contactNormalImpulse;
                }

                // handle high velocity and resting collisions separately
                if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) {
                    // high tangent velocity so clear cached contact tangent impulse
                    contact.tangentImpulse = 0;
                } else {
                    // solve resting collision constraints using Erin Catto's method (GDC08)
                    // tangent impulse tends to -tangentSpeed or +tangentSpeed
                    var contactTangentImpulse = contact.tangentImpulse;
                    contact.tangentImpulse = Common.clamp(contact.tangentImpulse + tangentImpulse, -maxFriction, maxFriction);
                    tangentImpulse = contact.tangentImpulse - contactTangentImpulse;
                }

                // total impulse from contact
                impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
                impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);

                // apply impulse from contact
                if (!(bodyA.isStatic || bodyA.isSleeping)) {
                    bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
                    bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
                    bodyA.anglePrev += Vector.cross(offsetA, impulse) * bodyA.inverseInertia;
                }

                if (!(bodyB.isStatic || bodyB.isSleeping)) {
                    bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
                    bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
                    bodyB.anglePrev -= Vector.cross(offsetB, impulse) * bodyB.inverseInertia;
                }
            }
        }
    };

})();
<commit_msg>improved position resolver<commit_after>/**
* The `Matter.Resolver` module contains methods for resolving collision pairs.
*
* @class Resolver
*/

var Resolver = {};

module.exports = Resolver;

var Vertices = require('../geometry/Vertices');
var Vector = require('../geometry/Vector');
var Common = require('../core/Common');
var Bounds = require('../geometry/Bounds');

(function() {

    Resolver._restingThresh = 4;
    Resolver._restingThreshTangent = 6;
    Resolver._positionDampen = 0.9;
    Resolver._positionWarming = 0.8;
    Resolver._frictionNormalMultiplier = 5;

    /**
     * Prepare pairs for position solving.
     * @method preSolvePosition
     * @param {pair[]} pairs
     */
    Resolver.preSolvePosition = function(pairs) {
        var i,
            pair,
            activeCount;

        // find total contacts on each body
        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive)
                continue;

            activeCount = pair.activeContacts.length;
            pair.collision.parentA.totalContacts += activeCount;
            pair.collision.parentB.totalContacts += activeCount;
        }
    };

    /**
     * Find a solution for pair positions.
     * @method solvePosition
     * @param {pair[]} pairs
     * @param {number} timeScale
     */
    Resolver.solvePosition = function(pairs, timeScale) {
        var i,
            pair,
            collision,
            bodyA,
            bodyB,
            normal,
            bodyBtoA,
            contactShare,
            positionImpulse,
            contactCount = {},
            tempA = Vector._temp[0],
            tempB = Vector._temp[1],
            tempC = Vector._temp[2],
            tempD = Vector._temp[3];

        // find impulses required to resolve penetration
        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            collision = pair.collision;
            bodyA = collision.parentA;
            bodyB = collision.parentB;
            normal = collision.normal;

            // get current separation between body edges involved in collision
            bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA),
                                    Vector.add(bodyA.positionImpulse,
                                        Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD);

            pair.separation = Vector.dot(normal, bodyBtoA);
        }

        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            collision = pair.collision;
            bodyA = collision.parentA;
            bodyB = collision.parentB;
            normal = collision.normal;
            positionImpulse = (pair.separation - pair.slop) * timeScale;

            if (bodyA.isStatic || bodyB.isStatic)
                positionImpulse *= 2;

            if (!(bodyA.isStatic || bodyA.isSleeping)) {
                contactShare = Resolver._positionDampen / bodyA.totalContacts;
                bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
                bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
            }

            if (!(bodyB.isStatic || bodyB.isSleeping)) {
                contactShare = Resolver._positionDampen / bodyB.totalContacts;
                bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
                bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
            }
        }
    };

    /**
     * Apply position resolution.
     * @method postSolvePosition
     * @param {body[]} bodies
     */
    Resolver.postSolvePosition = function(bodies) {
        for (var i = 0; i < bodies.length; i++) {
            var body = bodies[i];

            // reset contact count
            body.totalContacts = 0;

            if (body.positionImpulse.x !== 0 || body.positionImpulse.y !== 0) {
                // update body geometry
                for (var j = 0; j < body.parts.length; j++) {
                    var part = body.parts[j];
                    Vertices.translate(part.vertices, body.positionImpulse);
                    Bounds.update(part.bounds, part.vertices, body.velocity);
                    part.position.x += body.positionImpulse.x;
                    part.position.y += body.positionImpulse.y;
                }

                // move the body without changing velocity
                body.positionPrev.x += body.positionImpulse.x;
                body.positionPrev.y += body.positionImpulse.y;

                if (Vector.dot(body.positionImpulse, body.velocity) < 0) {
                    // reset cached impulse if the body has velocity along it
                    body.positionImpulse.x = 0;
                    body.positionImpulse.y = 0;
                } else {
                    // warm the next iteration
                    body.positionImpulse.x *= Resolver._positionWarming;
                    body.positionImpulse.y *= Resolver._positionWarming;
                }
            }
        }
    };

    /**
     * Prepare pairs for velocity solving.
     * @method preSolveVelocity
     * @param {pair[]} pairs
     */
    Resolver.preSolveVelocity = function(pairs) {
        var i,
            j,
            pair,
            contacts,
            collision,
            bodyA,
            bodyB,
            normal,
            tangent,
            contact,
            contactVertex,
            normalImpulse,
            tangentImpulse,
            offset,
            impulse = Vector._temp[0],
            tempA = Vector._temp[1];

        for (i = 0; i < pairs.length; i++) {
            pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            contacts = pair.activeContacts;
            collision = pair.collision;
            bodyA = collision.parentA;
            bodyB = collision.parentB;
            normal = collision.normal;
            tangent = collision.tangent;

            // resolve each contact
            for (j = 0; j < contacts.length; j++) {
                contact = contacts[j];
                contactVertex = contact.vertex;
                normalImpulse = contact.normalImpulse;
                tangentImpulse = contact.tangentImpulse;

                if (normalImpulse !== 0 || tangentImpulse !== 0) {
                    // total impulse from contact
                    impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
                    impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);

                    // apply impulse from contact
                    if (!(bodyA.isStatic || bodyA.isSleeping)) {
                        offset = Vector.sub(contactVertex, bodyA.position, tempA);
                        bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
                        bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
                        bodyA.anglePrev += Vector.cross(offset, impulse) * bodyA.inverseInertia;
                    }

                    if (!(bodyB.isStatic || bodyB.isSleeping)) {
                        offset = Vector.sub(contactVertex, bodyB.position, tempA);
                        bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
                        bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
                        bodyB.anglePrev -= Vector.cross(offset, impulse) * bodyB.inverseInertia;
                    }
                }
            }
        }
    };

    /**
     * Find a solution for pair velocities.
     * @method solveVelocity
     * @param {pair[]} pairs
     * @param {number} timeScale
     */
    Resolver.solveVelocity = function(pairs, timeScale) {
        var timeScaleSquared = timeScale * timeScale,
            impulse = Vector._temp[0],
            tempA = Vector._temp[1],
            tempB = Vector._temp[2],
            tempC = Vector._temp[3],
            tempD = Vector._temp[4],
            tempE = Vector._temp[5];

        for (var i = 0; i < pairs.length; i++) {
            var pair = pairs[i];

            if (!pair.isActive || pair.isSensor)
                continue;

            var collision = pair.collision,
                bodyA = collision.parentA,
                bodyB = collision.parentB,
                normal = collision.normal,
                tangent = collision.tangent,
                contacts = pair.activeContacts,
                contactShare = 1 / contacts.length;

            // update body velocities
            bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x;
            bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y;
            bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x;
            bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y;
            bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
            bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;

            // resolve each contact
            for (var j = 0; j < contacts.length; j++) {
                var contact = contacts[j],
                    contactVertex = contact.vertex,
                    offsetA = Vector.sub(contactVertex, bodyA.position, tempA),
                    offsetB = Vector.sub(contactVertex, bodyB.position, tempB),
                    velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity), tempC),
                    velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity), tempD),
                    relativeVelocity = Vector.sub(velocityPointA, velocityPointB, tempE),
                    normalVelocity = Vector.dot(normal, relativeVelocity);

                var tangentVelocity = Vector.dot(tangent, relativeVelocity),
                    tangentSpeed = Math.abs(tangentVelocity),
                    tangentVelocityDirection = Common.sign(tangentVelocity);

                // raw impulses
                var normalImpulse = (1 + pair.restitution) * normalVelocity,
                    normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * Resolver._frictionNormalMultiplier;

                // coulomb friction
                var tangentImpulse = tangentVelocity,
                    maxFriction = Infinity;

                if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) {
                    maxFriction = tangentSpeed;
                    tangentImpulse = Common.clamp(
                        pair.friction * tangentVelocityDirection * timeScaleSquared,
                        -maxFriction, maxFriction
                    );
                }

                // modify impulses accounting for mass, inertia and offset
                var oAcN = Vector.cross(offsetA, normal),
                    oBcN = Vector.cross(offsetB, normal),
                    share = contactShare / (bodyA.inverseMass + bodyB.inverseMass + bodyA.inverseInertia * oAcN * oAcN  + bodyB.inverseInertia * oBcN * oBcN);

                normalImpulse *= share;
                tangentImpulse *= share;

                // handle high velocity and resting collisions separately
                if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) {
                    // high normal velocity so clear cached contact normal impulse
                    contact.normalImpulse = 0;
                } else {
                    // solve resting collision constraints using Erin Catto's method (GDC08)
                    // impulse constraint tends to 0
                    var contactNormalImpulse = contact.normalImpulse;
                    contact.normalImpulse = Math.min(contact.normalImpulse + normalImpulse, 0);
                    normalImpulse = contact.normalImpulse - contactNormalImpulse;
                }

                // handle high velocity and resting collisions separately
                if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) {
                    // high tangent velocity so clear cached contact tangent impulse
                    contact.tangentImpulse = 0;
                } else {
                    // solve resting collision constraints using Erin Catto's method (GDC08)
                    // tangent impulse tends to -tangentSpeed or +tangentSpeed
                    var contactTangentImpulse = contact.tangentImpulse;
                    contact.tangentImpulse = Common.clamp(contact.tangentImpulse + tangentImpulse, -maxFriction, maxFriction);
                    tangentImpulse = contact.tangentImpulse - contactTangentImpulse;
                }

                // total impulse from contact
                impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse);
                impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse);

                // apply impulse from contact
                if (!(bodyA.isStatic || bodyA.isSleeping)) {
                    bodyA.positionPrev.x += impulse.x * bodyA.inverseMass;
                    bodyA.positionPrev.y += impulse.y * bodyA.inverseMass;
                    bodyA.anglePrev += Vector.cross(offsetA, impulse) * bodyA.inverseInertia;
                }

                if (!(bodyB.isStatic || bodyB.isSleeping)) {
                    bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass;
                    bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass;
                    bodyB.anglePrev -= Vector.cross(offsetB, impulse) * bodyB.inverseInertia;
                }
            }
        }
    };

})();
<|endoftext|>


<commit_before>const hsv2hsl = function(hue, sat, val) {
  return [
    hue,
    (sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue)) || 0,
    hue / 2
  ];
};

// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
const isOnePointZero = function(n) {
  return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
};

const isPercentage = function(n) {
  return typeof n === 'string' && n.indexOf('%') !== -1;
};

// Take input from [0, n] and return it as [0, 1]
const bound01 = function(value, max) {
  if (isOnePointZero(value)) value = '100%';

  const processPercent = isPercentage(value);
  value = Math.min(max, Math.max(0, parseFloat(value)));

  // Automatically convert percentage into number
  if (processPercent) {
    value = parseInt(value * max, 10) / 100;
  }

  // Handle floating point rounding errors
  if ((Math.abs(value - max) < 0.000001)) {
    return 1;
  }

  // Convert into [0, 1] range if it isn't already
  return (value % max) / parseFloat(max);
};

const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };

const toHex = function({ r, g, b }) {
  const hexOne = function(value) {
    value = Math.min(Math.round(value), 255);
    const high = Math.floor(value / 16);
    const low = value % 16;
    return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
  };

  if (isNaN(r) || isNaN(g) || isNaN(b)) return '';

  return '#' + hexOne(r) + hexOne(g) + hexOne(b);
};

const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };

const parseHexChannel = function(hex) {
  if (hex.length === 2) {
    return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
  }

  return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
};

const hsl2hsv = function(hue, sat, light) {
  sat = sat / 100;
  light = light / 100;
  let smin = sat;
  const lmin = Math.max(light, 0.01);
  let sv;
  let v;

  light *= 2;
  sat *= (light <= 1) ? light : 2 - light;
  smin *= lmin <= 1 ? lmin : 2 - lmin;
  v = (light + sat) / 2;
  sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);

  return {
    h: hue,
    s: sv * 100,
    v: v * 100
  };
};

// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
const rgb2hsv = function(r, g, b) {
  r = bound01(r, 255);
  g = bound01(g, 255);
  b = bound01(b, 255);

  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  let h, s;
  let v = max;

  const d = max - min;
  s = max === 0 ? 0 : d / max;

  if (max === min) {
    h = 0; // achromatic
  } else {
    switch (max) {
      case r:
        h = (g - b) / d + (g < b ? 6 : 0);
        break;
      case g:
        h = (b - r) / d + 2;
        break;
      case b:
        h = (r - g) / d + 4;
        break;
    }
    h /= 6;
  }

  return { h: Math.round(h * 360), s: Math.round(s * 100), v: Math.round(v * 100) };
};

// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
const hsv2rgb = function(h, s, v) {
  h = bound01(h, 360) * 6;
  s = bound01(s, 100);
  v = bound01(v, 100);

  const i = Math.floor(h);
  const f = h - i;
  const p = v * (1 - s);
  const q = v * (1 - f * s);
  const t = v * (1 - (1 - f) * s);
  const mod = i % 6;
  const r = [v, q, p, p, t, v][mod];
  const g = [t, v, v, q, p, p][mod];
  const b = [p, p, t, v, v, q][mod];

  return {
    r: Math.round(r * 255),
    g: Math.round(g * 255),
    b: Math.round(b * 255)
  };
};

export default class Color {
  constructor(options) {
    this._hue = 0;
    this._saturation = 100;
    this._value = 100;
    this._alpha = 100;

    this.enableAlpha = false;
    this.format = 'hex';
    this.value = '';

    options = options || {};

    for (let option in options) {
      if (options.hasOwnProperty(option)) {
        this[option] = options[option];
      }
    }

    this.doOnChange();
  }

  set(prop, value) {
    if (arguments.length === 1 && typeof prop === 'object') {
      for (let p in prop) {
        if (prop.hasOwnProperty(p)) {
          this.set(p, prop[p]);
        }
      }

      return;
    }

    this['_' + prop] = value;
    this.doOnChange();
  }

  get(prop) {
    return this['_' + prop];
  }

  toRgb() {
    return hsv2rgb(this._hue, this._saturation, this._value);
  }

  fromString(value) {
    if (!value) {
      this._hue = 0;
      this._saturation = 100;
      this._value = 100;

      this.doOnChange();
      return;
    }

    const fromHSV = (h, s, v) => {
      this._hue = h;
      this._saturation = s;
      this._value = v;

      this.doOnChange();
    };

    if (value.indexOf('hsl') !== -1) {
      const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));

      if (parts.length === 4) {
        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
      }
      if (parts.length >= 3) {
        const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
        fromHSV(h, s, v);
      }
    } else if (value.indexOf('hsv') !== -1) {
      const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));

      if (parts.length === 4) {
        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
      }
      if (parts.length >= 3) {
        fromHSV(parts[0], parts[1], parts[2]);
      }
    } else if (value.indexOf('rgb') !== -1) {
      const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));

      if (parts.length === 4) {
        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
      }
      if (parts.length >= 3) {
        const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
        fromHSV(h, s, v);
      }
    } else if (value.indexOf('#') !== -1) {
      const hex = value.replace('#', '').trim();
      let r, g, b;

      if (hex.length === 3) {
        r = parseHexChannel(hex[0] + hex[0]);
        g = parseHexChannel(hex[1] + hex[1]);
        b = parseHexChannel(hex[2] + hex[2]);
      } else if (hex.length === 6) {
        r = parseHexChannel(hex.substring(0, 2));
        g = parseHexChannel(hex.substring(2, 4));
        b = parseHexChannel(hex.substring(4));
      }

      const { h, s, v } = rgb2hsv(r, g, b);
      fromHSV(h, s, v);
    }
  }

  doOnChange() {
    const { _hue, _saturation, _value, _alpha, format } = this;

    if (this.enableAlpha) {
      switch (format) {
        case 'hsl': {
          const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
          this.value = `hsla(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%, ${ _alpha / 100})`;
          break;
        }
        case 'hsv':
          this.value = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha / 100})`;
          break;
        default: {
          const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
          this.value = `rgba(${r}, ${g}, ${b}, ${ _alpha / 100 })`;
        }
      }
    } else {
      switch (format) {
        case 'hsl': {
          const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
          this.value = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;
          break;
        }
        case 'hsv':
          this.value = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;
          break;
        case 'rgb': {
          const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
          this.value = `rgb(${r}, ${g}, ${b})`;
          break;
        }
        default:
          this.value = toHex(hsv2rgb(_hue, _saturation, _value));
      }
    }
  }
}
<commit_msg>Fix no-cond-assign error<commit_after>const hsv2hsl = function(hue, sat, val) {
  let sl, l;

  l = (2 - sat) * val;
  sl = sat * val;
  sl /= (l < 1) ? l : 2 - l;
  sl = sl || 0;
  l /= 2;
  return [hue, sl, l];
};

// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
const isOnePointZero = function(n) {
  return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
};

const isPercentage = function(n) {
  return typeof n === 'string' && n.indexOf('%') !== -1;
};

// Take input from [0, n] and return it as [0, 1]
const bound01 = function(value, max) {
  if (isOnePointZero(value)) value = '100%';

  const processPercent = isPercentage(value);
  value = Math.min(max, Math.max(0, parseFloat(value)));

  // Automatically convert percentage into number
  if (processPercent) {
    value = parseInt(value * max, 10) / 100;
  }

  // Handle floating point rounding errors
  if ((Math.abs(value - max) < 0.000001)) {
    return 1;
  }

  // Convert into [0, 1] range if it isn't already
  return (value % max) / parseFloat(max);
};

const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };

const toHex = function({ r, g, b }) {
  const hexOne = function(value) {
    value = Math.min(Math.round(value), 255);
    const high = Math.floor(value / 16);
    const low = value % 16;
    return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
  };

  if (isNaN(r) || isNaN(g) || isNaN(b)) return '';

  return '#' + hexOne(r) + hexOne(g) + hexOne(b);
};

const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };

const parseHexChannel = function(hex) {
  if (hex.length === 2) {
    return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
  }

  return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
};

const hsl2hsv = function(hue, sat, light) {
  sat = sat / 100;
  light = light / 100;
  let smin = sat;
  const lmin = Math.max(light, 0.01);
  let sv;
  let v;

  light *= 2;
  sat *= (light <= 1) ? light : 2 - light;
  smin *= lmin <= 1 ? lmin : 2 - lmin;
  v = (light + sat) / 2;
  sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);

  return {
    h: hue,
    s: sv * 100,
    v: v * 100
  };
};

// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
const rgb2hsv = function(r, g, b) {
  r = bound01(r, 255);
  g = bound01(g, 255);
  b = bound01(b, 255);

  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  let h, s;
  let v = max;

  const d = max - min;
  s = max === 0 ? 0 : d / max;

  if (max === min) {
    h = 0; // achromatic
  } else {
    switch (max) {
      case r:
        h = (g - b) / d + (g < b ? 6 : 0);
        break;
      case g:
        h = (b - r) / d + 2;
        break;
      case b:
        h = (r - g) / d + 4;
        break;
    }
    h /= 6;
  }

  return { h: Math.round(h * 360), s: Math.round(s * 100), v: Math.round(v * 100) };
};

// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
const hsv2rgb = function(h, s, v) {
  h = bound01(h, 360) * 6;
  s = bound01(s, 100);
  v = bound01(v, 100);

  const i = Math.floor(h);
  const f = h - i;
  const p = v * (1 - s);
  const q = v * (1 - f * s);
  const t = v * (1 - (1 - f) * s);
  const mod = i % 6;
  const r = [v, q, p, p, t, v][mod];
  const g = [t, v, v, q, p, p][mod];
  const b = [p, p, t, v, v, q][mod];

  return {
    r: Math.round(r * 255),
    g: Math.round(g * 255),
    b: Math.round(b * 255)
  };
};

export default class Color {
  constructor(options) {
    this._hue = 0;
    this._saturation = 100;
    this._value = 100;
    this._alpha = 100;

    this.enableAlpha = false;
    this.format = 'hex';
    this.value = '';

    options = options || {};

    for (let option in options) {
      if (options.hasOwnProperty(option)) {
        this[option] = options[option];
      }
    }

    this.doOnChange();
  }

  set(prop, value) {
    if (arguments.length === 1 && typeof prop === 'object') {
      for (let p in prop) {
        if (prop.hasOwnProperty(p)) {
          this.set(p, prop[p]);
        }
      }

      return;
    }

    this['_' + prop] = value;
    this.doOnChange();
  }

  get(prop) {
    return this['_' + prop];
  }

  toRgb() {
    return hsv2rgb(this._hue, this._saturation, this._value);
  }

  fromString(value) {
    if (!value) {
      this._hue = 0;
      this._saturation = 100;
      this._value = 100;

      this.doOnChange();
      return;
    }

    const fromHSV = (h, s, v) => {
      this._hue = h;
      this._saturation = s;
      this._value = v;

      this.doOnChange();
    };

    if (value.indexOf('hsl') !== -1) {
      const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));

      if (parts.length === 4) {
        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
      }
      if (parts.length >= 3) {
        const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
        fromHSV(h, s, v);
      }
    } else if (value.indexOf('hsv') !== -1) {
      const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));

      if (parts.length === 4) {
        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
      }
      if (parts.length >= 3) {
        fromHSV(parts[0], parts[1], parts[2]);
      }
    } else if (value.indexOf('rgb') !== -1) {
      const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
        .split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));

      if (parts.length === 4) {
        this._alpha = Math.floor(parseFloat(parts[3]) * 100);
      }
      if (parts.length >= 3) {
        const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
        fromHSV(h, s, v);
      }
    } else if (value.indexOf('#') !== -1) {
      const hex = value.replace('#', '').trim();
      let r, g, b;

      if (hex.length === 3) {
        r = parseHexChannel(hex[0] + hex[0]);
        g = parseHexChannel(hex[1] + hex[1]);
        b = parseHexChannel(hex[2] + hex[2]);
      } else if (hex.length === 6) {
        r = parseHexChannel(hex.substring(0, 2));
        g = parseHexChannel(hex.substring(2, 4));
        b = parseHexChannel(hex.substring(4));
      }

      const { h, s, v } = rgb2hsv(r, g, b);
      fromHSV(h, s, v);
    }
  }

  doOnChange() {
    const { _hue, _saturation, _value, _alpha, format } = this;

    if (this.enableAlpha) {
      switch (format) {
        case 'hsl': {
          const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
          this.value = `hsla(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%, ${ _alpha / 100})`;
          break;
        }
        case 'hsv':
          this.value = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha / 100})`;
          break;
        default: {
          const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
          this.value = `rgba(${r}, ${g}, ${b}, ${ _alpha / 100 })`;
        }
      }
    } else {
      switch (format) {
        case 'hsl': {
          const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
          this.value = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;
          break;
        }
        case 'hsv':
          this.value = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;
          break;
        case 'rgb': {
          const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
          this.value = `rgb(${r}, ${g}, ${b})`;
          break;
        }
        default:
          this.value = toHex(hsv2rgb(_hue, _saturation, _value));
      }
    }
  }
}
<|endoftext|>


<commit_before>define(["js/data/Entity", "js/data/validator/RegExValidator"], function (Entity, RegExValidator) {

    var SalutationMap = {
        "1": "mr",
        "2": "mrs",
        "3": "ms",
        "4": "company"
    };

    return Entity.inherit("sprd.entity.Person", {

        defaults: {
            salutation: null,
            firstName: '',
            lastName: ''
        },

        schema: {
            salutation: {type: String, required: false},
            firstName: String,
            lastName: String
        },

        validators: [
            new RegExValidator({
                field: "lastName",
                regEx: /^[0-9a-zA-Z]{0,30}$/,
                errorCode: 'lastNameError'
            }),
            new RegExValidator({
                field: "firstName",
                regEx: /^[0-9a-zA-Z]{0,30}$/,
                errorCode: 'firstNameError'
            })
        ],

        fullName: function () {
            return [(this.$.firstName || ""), (this.$.lastName || "")].join(" ");
        }.onChange("firstName", "lastName"),

        parse: function () {
            var data = this.callBase();
            data.salutation = (data.salutation || {}).id || null;
            return data;
        },

        compose: function () {
            var data = this.callBase();

            if (data.salutation) {
                data.salutation = {
                    id: data.salutation
                };
            }

            return  data;
        },

        contraction: function () {
            return SalutationMap[this.$.salutation];
        }.onChange("salutation")
    });
});<commit_msg>DEV-69489 - Input fields are not trimmed and show Validation error * removed length validation for person<commit_after>define(["js/data/Entity"], function (Entity) {

    var SalutationMap = {
        "1": "mr",
        "2": "mrs",
        "3": "ms",
        "4": "company"
    };

    return Entity.inherit("sprd.entity.Person", {

        defaults: {
            salutation: null,
            firstName: '',
            lastName: ''
        },

        schema: {
            salutation: {type: String, required: false},
            firstName: String,
            lastName: String
        },

        fullName: function () {
            return [(this.$.firstName || ""), (this.$.lastName || "")].join(" ");
        }.onChange("firstName", "lastName"),

        parse: function () {
            var data = this.callBase();
            data.salutation = (data.salutation || {}).id || null;
            return data;
        },

        compose: function () {
            var data = this.callBase();

            if (data.salutation) {
                data.salutation = {
                    id: data.salutation
                };
            }

            return  data;
        },

        contraction: function () {
            return SalutationMap[this.$.salutation];
        }.onChange("salutation")
    });
});<|endoftext|>


<commit_before>$(function() {
    // If we're not using the sidebar, expand the main content
    if ($("#no-sidebar").length > 0) {
        $("#content").toggleClass('col-md-9');
    }

    // If we're not using the breadcrumb, add 20px of margin to the body
    // to keep spacing the same from page to page
    if ($("#no-breadcrumb").length > 0) {
        $("#content").parent().css('margin-top', '20px');
    }

    // If the URL starts with "/weblog/" or "/about", gray out the
    // siggame tab
    if (window.location.pathname.match(/^\/weblog\//) != null ||
        window.location.pathname.match(/^\/about/) != null) {
        $("#siggame-tab").toggleClass("active");
    }

    // If the URL starts with "/profile/", gray out the profile tab
    if (window.location.pathname.match(/^\/profile/) != null) {
        $("#profile-tab").toggleClass("active");
    }

    // If the URL starts with "/competition"...
    if (window.location.pathname.match(/^\/competition/) != null) {
        $("#competition-tab").toggleClass("active");

        var competition_nav = $("#competition-nav > li");
        var code_nav = $("#code-nav > li");
        var team_nav = $("#team-nav > li");
        var registration_nav = $("#registration-nav > li");

        if (window.location.pathname.match(/^\/competition\/[\w\-]+\/$/)) {
            // Activate Competition Details nav button
            $(competition_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/games\/$/)) {
            // Activate Games nav button
            $(competition_nav[1]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/gamestats\/$/)) {
            // Activate Game Stats nav button
            $(competition_nav[2]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/submissions\/$/)) {
            // Activate Code Submissions nav button
            $(code_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/update-password\/$/)) {
            // Activate Update Repository Password nav button
            $(code_nav[2]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/teams\/$/)) {
            // Activate All Teams nav button
            $(team_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/freeagents\/$/)) {
            // Activate Free Agents nav button
            $(team_nav[1]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/team-leave\/$/)) {
            // Activate Team Leave nav button
            $(team_nav[3]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/unregister\/$/)) {
            // Activate Unregister nav button
            $(registration_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/team-create\/$/)) {
            // Activate Create Team nav button
            $(team_nav[2]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/create-repo\/$/)) {
            // Activate Create Repo nav button
            $(code_nav[0]).toggleClass("active");
        }
    }

    if (window.location.pathname.match(/^\/repo/) != null) {
        $("#competition-tab").toggleClass("active");
        $($("#code-nav > li")[1]).toggleClass("active");
    }

    $('#your-competitions').tooltip({placement: "left", delay: 1000});

    // If the URL starts with "/invitation", gray out the profile tab
    if (window.location.pathname.match(/^\/invitation/) != null) {
        $("#invitation-tab").toggleClass("active");
    }

    // If the URL starts with "/docs" gray out the documentation tab
    if (window.location.pathname.match(/^\/docs/) != null) {
        $("#documentation-tab").toggleClass("active");
    }

    // If the URL starts with "/accounts/email", gray out the profile tab
    if (window.location.pathname.match(/^\/accounts\/email/) != null) {
        $("#profile-tab").toggleClass("active");
    }

    // If the URL starts with "/social/connections", gray out the profile tab
    if (window.location.pathname.match(/^\/accounts\/social\//) != null) {
        $("#profile-tab").toggleClass("active");
    }

    // If the URL starts with "/accounts/password", gray out the profile tab
    if (window.location.pathname.match(/^\/accounts\/password\/(change|set)/) != null) {
        $("#profile-tab").toggleClass("active");
    }
});
<commit_msg>Highlight 'game' nav-pill when viewing game details<commit_after>$(function() {
    // If we're not using the sidebar, expand the main content
    if ($("#no-sidebar").length > 0) {
        $("#content").toggleClass('col-md-9');
    }

    // If we're not using the breadcrumb, add 20px of margin to the body
    // to keep spacing the same from page to page
    if ($("#no-breadcrumb").length > 0) {
        $("#content").parent().css('margin-top', '20px');
    }

    // If the URL starts with "/weblog/" or "/about", gray out the
    // siggame tab
    if (window.location.pathname.match(/^\/weblog\//) != null ||
        window.location.pathname.match(/^\/about/) != null) {
        $("#siggame-tab").toggleClass("active");
    }

    // If the URL starts with "/profile/", gray out the profile tab
    if (window.location.pathname.match(/^\/profile/) != null) {
        $("#profile-tab").toggleClass("active");
    }

    // If the URL starts with "/competition"...
    if (window.location.pathname.match(/^\/competition/) != null) {
        $("#competition-tab").toggleClass("active");

        var competition_nav = $("#competition-nav > li");
        var code_nav = $("#code-nav > li");
        var team_nav = $("#team-nav > li");
        var registration_nav = $("#registration-nav > li");

        if (window.location.pathname.match(/^\/competition\/[\w\-]+\/$/)) {
            // Activate Competition Details nav button
            $(competition_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/games?\//)) {
            // Activate Games nav button
            $(competition_nav[1]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/gamestats\/$/)) {
            // Activate Game Stats nav button
            $(competition_nav[2]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/submissions\/$/)) {
            // Activate Code Submissions nav button
            $(code_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/update-password\/$/)) {
            // Activate Update Repository Password nav button
            $(code_nav[2]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/teams\/$/)) {
            // Activate All Teams nav button
            $(team_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/freeagents\/$/)) {
            // Activate Free Agents nav button
            $(team_nav[1]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/team-leave\/$/)) {
            // Activate Team Leave nav button
            $(team_nav[3]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/unregister\/$/)) {
            // Activate Unregister nav button
            $(registration_nav[0]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/team-create\/$/)) {
            // Activate Create Team nav button
            $(team_nav[2]).toggleClass("active");
        } else if (window.location.pathname.match(/^\/competition\/[\w\-]+\/create-repo\/$/)) {
            // Activate Create Repo nav button
            $(code_nav[0]).toggleClass("active");
        }
    }

    if (window.location.pathname.match(/^\/repo/) != null) {
        $("#competition-tab").toggleClass("active");
        $($("#code-nav > li")[1]).toggleClass("active");
    }

    $('#your-competitions').tooltip({placement: "left", delay: 1000});

    // If the URL starts with "/invitation", gray out the profile tab
    if (window.location.pathname.match(/^\/invitation/) != null) {
        $("#invitation-tab").toggleClass("active");
    }

    // If the URL starts with "/docs" gray out the documentation tab
    if (window.location.pathname.match(/^\/docs/) != null) {
        $("#documentation-tab").toggleClass("active");
    }

    // If the URL starts with "/accounts/email", gray out the profile tab
    if (window.location.pathname.match(/^\/accounts\/email/) != null) {
        $("#profile-tab").toggleClass("active");
    }

    // If the URL starts with "/social/connections", gray out the profile tab
    if (window.location.pathname.match(/^\/accounts\/social\//) != null) {
        $("#profile-tab").toggleClass("active");
    }

    // If the URL starts with "/accounts/password", gray out the profile tab
    if (window.location.pathname.match(/^\/accounts\/password\/(change|set)/) != null) {
        $("#profile-tab").toggleClass("active");
    }
});
<|endoftext|>


<commit_before>function unbalanced_parentheses(text_code) {
    var tokens = (new BiwaScheme.Parser(text_code)).tokens;
    var parentheses = 0;
    var brakets = 0;
    for(var i=0; i<tokens.length; ++i) {
        switch(tokens[i]) {
            case "[": ++brakets; break;
            case "]": --brakets; break;
            case "(": ++parentheses; break;
            case ")": --parentheses; break;
        }
    }
    return parentheses != 0 || brakets != 0;
}
// -----------------------------------------------------------------------------
// S-Expression Tokenizer taken from LIPS interpreter
// Copyright (C) Jakub T. Jankiewicz
// released under MIT license
// -----------------------------------------------------------------------------
var tokenize = (function() {
    var pre_parse_re = /("(?:\\[\S\s]|[^"])*"|\/(?! )[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|\(|\)|$)|;.*)/g;
    var tokens_re = /("(?:\\[\S\s]|[^"])*"|\/(?! )[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|\(|\)|$)|\(|\)|'|"(?:\\[\S\s]|[^"])+|\n|(?:\\[\S\s]|[^"])*"|;.*|(?:[-+]?(?:(?:\.[0-9]+|[0-9]+\.[0-9]+)(?:[eE][-+]?[0-9]+)?)|[0-9]+\.)[0-9]|\.|,@|,|`|[^(\s)]+)/gim;
    // ----------------------------------------------------------------------
    function last_item(array) {
        return array[array.length - 1];
    }
    // ----------------------------------------------------------------------
    return function tokens(str) {
        str = str.replace(/\n\r|\r/g, '\n');
        var count = 0;
        var line = 0;
        var tokens = [];
        var prev_string;
        var current_line = [];
        var col = 0;
        str.split(pre_parse_re).filter(Boolean).forEach(function(string) {
            if (string.match(pre_parse_re)) {
                col = 0;
                if (current_line.length) {
                    var last_token = last_item(current_line);
                    if (last_token.token.match(/\n/)) {
                        var last_line = last_token.token.split('\n').pop();
                        col += last_line.length;
                    } else {
                        col += last_token.token.length;
                    }
                    col += last_token.col;
                }
                var token = {
                    col,
                    line,
                    token: string,
                    offset: count
                };
                tokens.push(token);
                current_line.push(token);
                count += string.length;
                col += string.length;
                line += (string.match("\n") || []).length;
                return;
            }
            string.split(tokens_re).filter(Boolean).forEach(function(string) {
                var token = {
                    col,
                    line,
                    token: string,
                    offset: count
                };
                col += string.length;
                count += string.length;
                tokens.push(token);
                current_line.push(token);
                if (string === '\n') {
                    ++line;
                    current_line = [];
                    col = 0;
                }
            });
        });
        return tokens;
    };
})();
//--------------------------------------------------------------------------
function sexp(tokens) {
   var count = 1;
   var i = tokens.length;
   while (count > 0) {
       var token = tokens[--i];
       if (!token) {
           return;
       }
       if (token.token === '(') {
           count--;
       } else if (token.token == ')') {
           count++;
       }
   }
   return tokens.slice(i);
}
//--------------------------------------------------------------------------
function indent(code, level, offset) {
   var specials = ['define', 'lambda', 'let', 'let*', 'letrec', 'define-macro'];
   var tokens = tokenize(code, true);
   var last_sexpr = sexp(tokens);
   var lines = code.split('\n');
   var prev_line = lines[lines.length - 1];
   var parse = prev_line.match(/^(\s*)/);
   var spaces = parse[1].length || offset;
   if (last_sexpr) {
       if (last_sexpr[0].line > 0) {
           offset = 0;
       }
       if (last_sexpr.length === 1) {
           return offset + last_sexpr[0].col + 1;
       } else if (specials.indexOf(last_sexpr[1].token) !== -1) {
           return offset + last_sexpr[0].col + level;
       } else if (last_sexpr[0].line < last_sexpr[1].line) {
           return offset + last_sexpr[0].col + 1;
       } else if (last_sexpr.length > 3 && last_sexpr[1].line === last_sexpr[3].line) {
           if (last_sexpr[1].token === '(') {
               return offset + last_sexpr[1].col;
           }
           return offset + last_sexpr[3].col;
       } else if (last_sexpr[0].line === last_sexpr[1].line) {
           return offset + level + last_sexpr[0].col;
       } else {
           var next_tokens = last_sexpr.slice(2);
           for (var i in next_tokens) {
               var token = next_tokens[i];
               if (token.token.trim()) {
                   return token.col;
               }
           }
       }
   } else {
       return 0;
   }
   return spaces + level;
}
//--------------------------------------------------------------------------
jQuery(document).ready(function($, undefined) {
    $.terminal.syntax("scheme");
    var prompt = 'biwascheme> ';
    //NOTE: $ is jQuery in this scope
    var trace = false;
    var bscheme = new BiwaScheme.Interpreter(function(e, state) {
       console.error(e);
       term.error(e.message);
    });

    BiwaScheme.Console.puts = function(string) {
        term.echo(string);
    };
    BiwaScheme.Port.current_output = new BiwaScheme.Port.CustomOutput(
        BiwaScheme.Console.puts
    );
    BiwaScheme.Port.current_input = new BiwaScheme.Port.CustomInput(function(callback){
        term.read('read> ', callback);
    });
    if (window.Prism) {
        // use symbols
        Prism.languages.insertBefore('scheme', 'string', {
            symbol: {
                pattern: /'[^\s()]+/g,
                greedy: true
            }
        });
        // add define-macro to list of keywords
        Prism.languages.scheme.keyword.pattern = /(\()(?:define(?:-syntax|-library|-values)?|(?:case-)?lambda|let(?:\*|rec)?(?:-values)?|else|if|cond|begin|delay(?:-force)?|parameterize|define-macro|guard|set!|(?:quasi-)?quote|syntax-rules)(?=[()\s])/;
    }
    var position;
    var timer;
    var term = $('#term').terminal(function(code, term) {
        try {
            if (trace) {
                var opc = biwascheme.compile(code);
                var dump_opc = (new BiwaScheme.Dumper()).dump_opc(opc);
                term.echo(dump_opc, {raw: true});
            }
            var result = bscheme.evaluate(code, function(result) {
                if (result !== undefined && result !== BiwaScheme.undef) {
                    term.echo('=> ' + BiwaScheme.write_ss(result));
                }
            });
        } catch(e) {
            term.error(e.message);
            throw(e);
        }
    }, {
        mobileIngoreAutoSpace: [',', '.', ')'],
        keymap: {
            ENTER: function(e, original) {
                if (unbalanced_parentheses(this.get_command())) {
                    var i = indent(term.before_cursor(), 2, prompt.length);
                    this.insert('\n' + (new Array(i + 1).join(' ')));
                } else {
                    original();
                }
            }
        },
        onPaste: function(e) {
            if (e.text) {
                var code = e.text;
                var lines = code.split('\n').map(function(line) {
                    return line.trim();
                });
                var output = '';
                var prompt = this.get_prompt();
                for (var i = 1; i < lines.length; ++i) {
                    var c = lines.slice(0, i).join('\n');
                    var ind = indent(c, 2, prompt.length);
                    var spaces = new Array(ind + 1).join(' ');
                    lines[i] = spaces + lines[i];
                }
                return lines.join('\n');
            }
        },
        keydown: function(e) {
            if (position !== false) {
                term.set_position(position);
                position = false;
            }
        },
        keypress: function(e) {
            var term = this;
            if (e.key == ')') {
                setTimeout(function() {
                    position = term.get_position();
                    var command = term.get_command().substring(0, position);
                    var len = command.split(/\n/)[0].length;
                    var tokens = tokenize(command);
                    var count = 1;
                    var token;
                    var i = tokens.length - 1;
                    while (count > 0) {
                        token = tokens[--i];
                        if (!token) {
                            return;
                        }
                        if (token.token === '(') {
                            count--;
                        } else if (token.token == ')') {
                            count++;
                        }
                    }
                    if (token.token == '(' && count === 0) {
                        clearTimeout(timer);
                        setTimeout(function() {
                            var offset = token.offset;
                            term.set_position(offset);
                            timer = setTimeout(function() {
                                term.set_position(position);
                                position = false;
                            }, 200);
                        }, 0);
                    }
                }, 0);
            } else {
                position = false;
            }
        },
        greetings: false,
        height: 250,
        name: 'biwa',
        exit: false,
        prompt: prompt
    });
    // we don't want formatting on version number
    term.echo('BiwaScheme Interpreter version ' + BiwaScheme.Version, {
        formatters: false
    });
    // run trace mode
    BiwaScheme.define_libfunc("trace", 0, 0, function(args) {
        trace = !trace;
        return BiwaScheme.undef;
    });
    // redefine sleep it sould pause terminal
    BiwaScheme.define_libfunc("sleep", 1, 1, function(ar){
        var sec = ar[0];
        BiwaScheme.assert_real(sec);
        term.pause();
        return new BiwaScheme.Pause(function(pause){
            setTimeout(function(){
                term.resume();
                pause.resume(BiwaScheme.nil);
            }, sec * 1000);
        });
    });
    /*
    // load should pause terminal
    BiwaScheme.define_libfunc("load", 1, 1, function(ar, intp){
        var path = ar[0];
        assert_string(path);
        term.pause();
        return new BiwaScheme.Pause(function(pause){
            $.ajax({
                url: path,
                processData: false,
                success: function(data) {
                    term.resume();
                    term.echo(data);
                    term.echo(pause instanceof BiwaScheme.Pause);
                    try {
                        // throw too much recursion here
                        bscheme.evaluate(data, function() {
                            return pause.resume(BiwaScheme.undef);
                        });
                        term.echo(path + ' loaded');
                    } catch(e) {
                        term.error(e.message);
                        throw(e);
                    }
                },
                error: function(xhr, stat) {
                    term.error("[AJAX] " + stat + " server reponse: \n" +
                               xhr.reponseText);
                }});
        });
    });
    */
    // clear terminal
    BiwaScheme.define_libfunc('clear', 0, 0, function(args) {
        term.clear();
        return BiwaScheme.undef;
    });

    // return all procedures from global environment
    BiwaScheme.define_libfunc('env', 0, 0, function(args) {
        var result = new Array();
        for(fun in window.BiwaScheme.CoreEnv) {
            result[result.length] = fun;
        }
        return BiwaScheme.array_to_list(result);
    });

    // return list of object properties like dir from python
    BiwaScheme.define_libfunc('dir', 1, 1, function(args) {
        var result = [];
        var object = args[0];
        for (i in object) {
            result.push(i);
        }
        return BiwaScheme.array_to_list(result);
    });

    // check if object is in list
    BiwaScheme.define_libfunc('contains?', 2, 2, function(args) {
        assert_list(args[1]);
        return $.inArray(args[0], args[1].to_array()) != -1;
    });

    // concatenate two or more string
    BiwaScheme.define_libfunc("concat", 1, null, function(args) {
        for (var i=args.length; i--;) {
            assert_string(args[i]);
        }
        return args.length == 1 ? args[0] : args.join('');
    });

    BiwaScheme.define_libfunc("join", 2, 2, function(args) {
        assert_list(args[1]);
        assert_string(args[0]);
        var array = args[1].to_array();
        for (var i=array.length; i--;) {
            assert_string(array[i]);
        }
        return array.join(args[0]);
    });

    BiwaScheme.define_libfunc("split", 2, 2, function(args) {
        assert_string(args[0]);
        assert_string(args[1]);
        var result = args[1].split(args[0]);
        return result.to_list();
    });

});
<commit_msg>website: Fix regression<commit_after>function unbalanced_parentheses(text_code) {
    var tokens = (new BiwaScheme.Parser(text_code)).tokens;
    var parentheses = 0;
    var brakets = 0;
    for(var i=0; i<tokens.length; ++i) {
        switch(tokens[i]) {
            case "[": ++brakets; break;
            case "]": --brakets; break;
            case "(": ++parentheses; break;
            case ")": --parentheses; break;
        }
    }
    return parentheses != 0 || brakets != 0;
}
// -----------------------------------------------------------------------------
// S-Expression Tokenizer taken from LIPS interpreter
// Copyright (C) Jakub T. Jankiewicz
// released under MIT license
// -----------------------------------------------------------------------------
var tokenize = (function() {
    var pre_parse_re = /("(?:\\[\S\s]|[^"])*"|\/(?! )[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|\(|\)|$)|;.*)/g;
    var tokens_re = /("(?:\\[\S\s]|[^"])*"|\/(?! )[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|\(|\)|$)|\(|\)|'|"(?:\\[\S\s]|[^"])+|\n|(?:\\[\S\s]|[^"])*"|;.*|(?:[-+]?(?:(?:\.[0-9]+|[0-9]+\.[0-9]+)(?:[eE][-+]?[0-9]+)?)|[0-9]+\.)[0-9]|\.|,@|,|`|[^(\s)]+)/gim;
    // ----------------------------------------------------------------------
    function last_item(array) {
        return array[array.length - 1];
    }
    // ----------------------------------------------------------------------
    return function tokens(str) {
        str = str.replace(/\n\r|\r/g, '\n');
        var count = 0;
        var line = 0;
        var tokens = [];
        var prev_string;
        var current_line = [];
        var col = 0;
        str.split(pre_parse_re).filter(Boolean).forEach(function(string) {
            if (string.match(pre_parse_re)) {
                col = 0;
                if (current_line.length) {
                    var last_token = last_item(current_line);
                    if (last_token.token.match(/\n/)) {
                        var last_line = last_token.token.split('\n').pop();
                        col += last_line.length;
                    } else {
                        col += last_token.token.length;
                    }
                    col += last_token.col;
                }
                var token = {
                    col,
                    line,
                    token: string,
                    offset: count
                };
                tokens.push(token);
                current_line.push(token);
                count += string.length;
                col += string.length;
                line += (string.match("\n") || []).length;
                return;
            }
            string.split(tokens_re).filter(Boolean).forEach(function(string) {
                var token = {
                    col,
                    line,
                    token: string,
                    offset: count
                };
                col += string.length;
                count += string.length;
                tokens.push(token);
                current_line.push(token);
                if (string === '\n') {
                    ++line;
                    current_line = [];
                    col = 0;
                }
            });
        });
        return tokens;
    };
})();
//--------------------------------------------------------------------------
function sexp(tokens) {
   var count = 1;
   var i = tokens.length;
   while (count > 0) {
       var token = tokens[--i];
       if (!token) {
           return;
       }
       if (token.token === '(') {
           count--;
       } else if (token.token == ')') {
           count++;
       }
   }
   return tokens.slice(i);
}
//--------------------------------------------------------------------------
function indent(code, level, offset) {
   var specials = ['define', 'lambda', 'let', 'let*', 'letrec', 'define-macro'];
   var tokens = tokenize(code, true);
   var last_sexpr = sexp(tokens);
   var lines = code.split('\n');
   var prev_line = lines[lines.length - 1];
   var parse = prev_line.match(/^(\s*)/);
   var spaces = parse[1].length || offset;
   if (last_sexpr) {
       if (last_sexpr[0].line > 0) {
           offset = 0;
       }
       if (last_sexpr.length === 1) {
           return offset + last_sexpr[0].col + 1;
       } else if (specials.indexOf(last_sexpr[1].token) !== -1) {
           return offset + last_sexpr[0].col + level;
       } else if (last_sexpr[0].line < last_sexpr[1].line) {
           return offset + last_sexpr[0].col + 1;
       } else if (last_sexpr.length > 3 && last_sexpr[1].line === last_sexpr[3].line) {
           if (last_sexpr[1].token === '(') {
               return offset + last_sexpr[1].col;
           }
           return offset + last_sexpr[3].col;
       } else if (last_sexpr[0].line === last_sexpr[1].line) {
           return offset + level + last_sexpr[0].col;
       } else {
           var next_tokens = last_sexpr.slice(2);
           for (var i in next_tokens) {
               var token = next_tokens[i];
               if (token.token.trim()) {
                   return token.col;
               }
           }
       }
   } else {
       return 0;
   }
   return spaces + level;
}
//--------------------------------------------------------------------------
jQuery(document).ready(function($, undefined) {
    $.terminal.syntax("scheme");
    var prompt = 'biwascheme> ';
    //NOTE: $ is jQuery in this scope
    var trace = false;
    var bscheme = new BiwaScheme.Interpreter(function(e, state) {
       console.error(e);
       term.error(e.message);
    });

    // TODO v0.7.1: This is no longer needed
    if (Console) BiwaScheme.Console = Console;

    BiwaScheme.Console.puts = function(string) {
        term.echo(string);
    };
    BiwaScheme.Port.current_output = new BiwaScheme.Port.CustomOutput(
        BiwaScheme.Console.puts
    );
    BiwaScheme.Port.current_input = new BiwaScheme.Port.CustomInput(function(callback){
        term.read('read> ', callback);
    });
    if (window.Prism) {
        // use symbols
        Prism.languages.insertBefore('scheme', 'string', {
            symbol: {
                pattern: /'[^\s()]+/g,
                greedy: true
            }
        });
        // add define-macro to list of keywords
        Prism.languages.scheme.keyword.pattern = /(\()(?:define(?:-syntax|-library|-values)?|(?:case-)?lambda|let(?:\*|rec)?(?:-values)?|else|if|cond|begin|delay(?:-force)?|parameterize|define-macro|guard|set!|(?:quasi-)?quote|syntax-rules)(?=[()\s])/;
    }
    var position;
    var timer;
    var term = $('#term').terminal(function(code, term) {
        try {
            if (trace) {
                var opc = biwascheme.compile(code);
                var dump_opc = (new BiwaScheme.Dumper()).dump_opc(opc);
                term.echo(dump_opc, {raw: true});
            }
            var result = bscheme.evaluate(code, function(result) {
                if (result !== undefined && result !== BiwaScheme.undef) {
                    term.echo('=> ' + BiwaScheme.write_ss(result));
                }
            });
        } catch(e) {
            term.error(e.message);
            throw(e);
        }
    }, {
        mobileIngoreAutoSpace: [',', '.', ')'],
        keymap: {
            ENTER: function(e, original) {
                if (unbalanced_parentheses(this.get_command())) {
                    var i = indent(term.before_cursor(), 2, prompt.length);
                    this.insert('\n' + (new Array(i + 1).join(' ')));
                } else {
                    original();
                }
            }
        },
        onPaste: function(e) {
            if (e.text) {
                var code = e.text;
                var lines = code.split('\n').map(function(line) {
                    return line.trim();
                });
                var output = '';
                var prompt = this.get_prompt();
                for (var i = 1; i < lines.length; ++i) {
                    var c = lines.slice(0, i).join('\n');
                    var ind = indent(c, 2, prompt.length);
                    var spaces = new Array(ind + 1).join(' ');
                    lines[i] = spaces + lines[i];
                }
                return lines.join('\n');
            }
        },
        keydown: function(e) {
            if (position !== false) {
                term.set_position(position);
                position = false;
            }
        },
        keypress: function(e) {
            var term = this;
            if (e.key == ')') {
                setTimeout(function() {
                    position = term.get_position();
                    var command = term.get_command().substring(0, position);
                    var len = command.split(/\n/)[0].length;
                    var tokens = tokenize(command);
                    var count = 1;
                    var token;
                    var i = tokens.length - 1;
                    while (count > 0) {
                        token = tokens[--i];
                        if (!token) {
                            return;
                        }
                        if (token.token === '(') {
                            count--;
                        } else if (token.token == ')') {
                            count++;
                        }
                    }
                    if (token.token == '(' && count === 0) {
                        clearTimeout(timer);
                        setTimeout(function() {
                            var offset = token.offset;
                            term.set_position(offset);
                            timer = setTimeout(function() {
                                term.set_position(position);
                                position = false;
                            }, 200);
                        }, 0);
                    }
                }, 0);
            } else {
                position = false;
            }
        },
        greetings: false,
        height: 250,
        name: 'biwa',
        exit: false,
        prompt: prompt
    });
    // we don't want formatting on version number
    term.echo('BiwaScheme Interpreter version ' + BiwaScheme.Version, {
        formatters: false
    });
    // run trace mode
    BiwaScheme.define_libfunc("trace", 0, 0, function(args) {
        trace = !trace;
        return BiwaScheme.undef;
    });
    // redefine sleep it sould pause terminal
    BiwaScheme.define_libfunc("sleep", 1, 1, function(ar){
        var sec = ar[0];
        BiwaScheme.assert_real(sec);
        term.pause();
        return new BiwaScheme.Pause(function(pause){
            setTimeout(function(){
                term.resume();
                pause.resume(BiwaScheme.nil);
            }, sec * 1000);
        });
    });
    /*
    // load should pause terminal
    BiwaScheme.define_libfunc("load", 1, 1, function(ar, intp){
        var path = ar[0];
        assert_string(path);
        term.pause();
        return new BiwaScheme.Pause(function(pause){
            $.ajax({
                url: path,
                processData: false,
                success: function(data) {
                    term.resume();
                    term.echo(data);
                    term.echo(pause instanceof BiwaScheme.Pause);
                    try {
                        // throw too much recursion here
                        bscheme.evaluate(data, function() {
                            return pause.resume(BiwaScheme.undef);
                        });
                        term.echo(path + ' loaded');
                    } catch(e) {
                        term.error(e.message);
                        throw(e);
                    }
                },
                error: function(xhr, stat) {
                    term.error("[AJAX] " + stat + " server reponse: \n" +
                               xhr.reponseText);
                }});
        });
    });
    */
    // clear terminal
    BiwaScheme.define_libfunc('clear', 0, 0, function(args) {
        term.clear();
        return BiwaScheme.undef;
    });

    // return all procedures from global environment
    BiwaScheme.define_libfunc('env', 0, 0, function(args) {
        var result = new Array();
        for(fun in window.BiwaScheme.CoreEnv) {
            result[result.length] = fun;
        }
        return BiwaScheme.array_to_list(result);
    });

    // return list of object properties like dir from python
    BiwaScheme.define_libfunc('dir', 1, 1, function(args) {
        var result = [];
        var object = args[0];
        for (i in object) {
            result.push(i);
        }
        return BiwaScheme.array_to_list(result);
    });

    // check if object is in list
    BiwaScheme.define_libfunc('contains?', 2, 2, function(args) {
        assert_list(args[1]);
        return $.inArray(args[0], args[1].to_array()) != -1;
    });

    // concatenate two or more string
    BiwaScheme.define_libfunc("concat", 1, null, function(args) {
        for (var i=args.length; i--;) {
            assert_string(args[i]);
        }
        return args.length == 1 ? args[0] : args.join('');
    });

    BiwaScheme.define_libfunc("join", 2, 2, function(args) {
        assert_list(args[1]);
        assert_string(args[0]);
        var array = args[1].to_array();
        for (var i=array.length; i--;) {
            assert_string(array[i]);
        }
        return array.join(args[0]);
    });

    BiwaScheme.define_libfunc("split", 2, 2, function(args) {
        assert_string(args[0]);
        assert_string(args[1]);
        var result = args[1].split(args[0]);
        return result.to_list();
    });

});
<|endoftext|>


<commit_before>import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { notification } from "antd";
import axios from "axios";
import { cartUpdated } from "../../utilities/events-utilities";
import { setBaseUrl } from "../../utilities/url-utilities";

const AJAX_URL = setBaseUrl(`/ajax/cart`);

export const cartApi = createApi({
  reducerPath: `cartApi`,
  baseQuery: fetchBaseQuery({ baseUrl: AJAX_URL }),
  tagTypes: ["Cart"],
  endpoints: (build) => ({
    getCartSamples: build.query({
      query: () => ({ url: "/all-samples" }),
    }),
  }),
});

export const { useGetCartSamplesQuery } = cartApi;

const updateCart = (data) => {
  data.notifications.forEach((n) => notification[n.type](n));
  cartUpdated(data.count);
  return data.count;
};

/**
 * Add samples for a project to the cart.
 * @param {number} projectId Identifier for the project the samples are from.
 * @param {array} samples array of sample {ids } to add to cart.
 * @returns {Promise<{count: any}>}
 */
export const putSampleInCart = async (projectId, samples) => {
  const { data } = await axios.post(AJAX_URL, {
    projectId,
    sampleIds: samples.map((s) => s.id),
  });
  return updateCart(data);
};

/**
 * Get the current number of samples in the cart
 * @returns {Promise<{count: any}>}
 */
export const getCartCount = async () => {
  const { data: count } = await axios.get(`${AJAX_URL}/count`);
  cartUpdated(count);
  return count;
};

/**
 * Get the current state of the cart.
 * @returns {Promise<void | never>}
 */
export const getCart = async () =>
  axios.get(`${AJAX_URL}`).then((response) => response.data);

/**
 * Get a list of projects identifiers that are in the cart
 * @returns {Promise<{ids: *}>}
 */
export const getCartIds = async () =>
  axios.get(`${AJAX_URL}/ids`).then((response) => ({ ids: response.data }));

/**
 * Get the samples for projects by their project identifiers.
 * @param ids - List of identifiers for projects in the cart
 * @returns {Promise<AxiosResponse<any>>}
 */
export const getSamplesForProjects = async (ids) =>
  axios
    .get(`${AJAX_URL}/samples?${ids.map((id) => `ids=${id}`).join("&")}`)
    .then(({ data }) =>
      data
        .map((project) => {
          const { samples, ...p } = project;
          return project.samples.map((sample) => ({ ...sample, project: p }));
        })
        .flat()
    );

/**
 * Remove all samples from the cart
 */
export const emptyCart = async () => axios.delete(`${AJAX_URL}`);

/**
 * Remove an individual sample from the cart.
 * @param {number} projectId - Identifier for a project
 * @param {number} sampleId - Identifier for a sample
 * @returns {Promise<* | never>}
 */
export const removeSample = async (projectId, sampleId) => {
  const { data } = await axios.delete(`${AJAX_URL}/sample/${sampleId}`);
  return updateCart(data);
};

/**
 * Remove an entire project from the cart.
 * @param {number} id - Identifier for a sample
 * @returns {Promise<{count: any}>}
 */
export const removeProject = async (id) => {
  const { data } = await axios.delete(`${AJAX_URL}/project?id=${id}`);
  return updateCart(data);
};
<commit_msg>Updated JSDoc<commit_after>import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { notification } from "antd";
import axios from "axios";
import { cartUpdated } from "../../utilities/events-utilities";
import { setBaseUrl } from "../../utilities/url-utilities";

const AJAX_URL = setBaseUrl(`/ajax/cart`);

/**
 * Redux Cart API
 * @type {Api<(args: (string | FetchArgs), api: BaseQueryApi, extraOptions: {}) => MaybePromise<QueryReturnValue<unknown, FetchBaseQueryError, FetchBaseQueryMeta>>, {getCartSamples: *}, string, string, typeof coreModuleName> | Api<(args: (string | FetchArgs), api: BaseQueryApi, extraOptions: {}) => MaybePromise<QueryReturnValue<unknown, FetchBaseQueryError, FetchBaseQueryMeta>>, {getCartSamples: *}, string, string, typeof coreModuleName | typeof reactHooksModuleName>}
 */
export const cartApi = createApi({
  reducerPath: `cartApi`,
  baseQuery: fetchBaseQuery({ baseUrl: AJAX_URL }),
  tagTypes: ["Cart"],
  endpoints: (build) => ({
    /*
    Get all the samples in the cart.  This is specially formatted for the create new project page
    where samples need to be broken down into locked and unlocked samples.
     */
    getCartSamples: build.query({
      query: () => ({ url: "/all-samples" }),
    }),
  }),
});

export const { useGetCartSamplesQuery } = cartApi;

const updateCart = (data) => {
  data.notifications.forEach((n) => notification[n.type](n));
  cartUpdated(data.count);
  return data.count;
};

/**
 * Add samples for a project to the cart.
 * @param {number} projectId Identifier for the project the samples are from.
 * @param {array} samples array of sample {ids } to add to cart.
 * @returns {Promise<{count: any}>}
 */
export const putSampleInCart = async (projectId, samples) => {
  const { data } = await axios.post(AJAX_URL, {
    projectId,
    sampleIds: samples.map((s) => s.id),
  });
  return updateCart(data);
};

/**
 * Get the current number of samples in the cart
 * @returns {Promise<{count: any}>}
 */
export const getCartCount = async () => {
  const { data: count } = await axios.get(`${AJAX_URL}/count`);
  cartUpdated(count);
  return count;
};

/**
 * Get the current state of the cart.
 * @returns {Promise<void | never>}
 */
export const getCart = async () =>
  axios.get(`${AJAX_URL}`).then((response) => response.data);

/**
 * Get a list of projects identifiers that are in the cart
 * @returns {Promise<{ids: *}>}
 */
export const getCartIds = async () =>
  axios.get(`${AJAX_URL}/ids`).then((response) => ({ ids: response.data }));

/**
 * Get the samples for projects by their project identifiers.
 * @param ids - List of identifiers for projects in the cart
 * @returns {Promise<AxiosResponse<any>>}
 */
export const getSamplesForProjects = async (ids) =>
  axios
    .get(`${AJAX_URL}/samples?${ids.map((id) => `ids=${id}`).join("&")}`)
    .then(({ data }) =>
      data
        .map((project) => {
          const { samples, ...p } = project;
          return project.samples.map((sample) => ({ ...sample, project: p }));
        })
        .flat()
    );

/**
 * Remove all samples from the cart
 */
export const emptyCart = async () => axios.delete(`${AJAX_URL}`);

/**
 * Remove an individual sample from the cart.
 * @param {number} projectId - Identifier for a project
 * @param {number} sampleId - Identifier for a sample
 * @returns {Promise<* | never>}
 */
export const removeSample = async (projectId, sampleId) => {
  const { data } = await axios.delete(`${AJAX_URL}/sample/${sampleId}`);
  return updateCart(data);
};

/**
 * Remove an entire project from the cart.
 * @param {number} id - Identifier for a sample
 * @returns {Promise<{count: any}>}
 */
export const removeProject = async (id) => {
  const { data } = await axios.delete(`${AJAX_URL}/project?id=${id}`);
  return updateCart(data);
};
<|endoftext|>


<commit_before>import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { notification } from "antd";
import axios from "axios";
import { cartUpdated } from "../../utilities/events-utilities";
import { setBaseUrl } from "../../utilities/url-utilities";

const AJAX_URL = setBaseUrl(`/ajax/cart`);

export const cartApi = createApi({
  reducerPath: `cartApi`,
  baseQuery: fetchBaseQuery({ baseUrl: AJAX_URL }),
  tagTypes: ["Samples", "CartCount"],
  endpoints: (build) => ({
    /*
   Get all the samples in the cart.  This is specially formatted for the create new project page
   where samples need to be broken down into locked and unlocked samples.
    */
    getCartSamples: build.query({
      query: () => ({ url: "/all-samples" }),
    }),
    count: build.query({
      query: () => ({ url: "/count" }),
      providesTags: ["CartCount"],
      transformResponse: (response) => {
        cartUpdated(response);
        return response;
      },
    }),
    getCart: build.query({
      query: () => ({
        url: "/samples",
      }),
      providesTags: (result) =>
        result
          ? [
              ...result.map(({ id }) => ({ type: "Samples", id })),
              { type: "Samples", id: "LIST" },
            ]
          : [{ type: "Samples", id: "LIST" }],
      transformResponse(response, meta) {
        return response
          .map((project) => {
            const { samples, ...p } = project;
            return project.samples.map((sample) => ({ ...sample, project: p }));
          })
          .flat();
      },
    }),
    empty: build.mutation({
      query: () => ({ url: "", method: "DELETE" }),
      invalidatesTags: () => ["CartCount", "Samples"],
      transformResponse: (response) => {
        cartUpdated(0);
        return response;
      },
    }),
    removeProject: build.mutation({
      query: ({ id }) => ({
        url: "/project",
        method: "DELETE",
        params: { id },
      }),
      invalidatesTags: [{ type: "Samples", id: "LIST" }, "CartCount"],
      transformResponse: (response) => {
        cartUpdated(response.count);
        return response.notifications[0];
      },
    }),
    removeSample: build.mutation({
      query: ({ sampleId }) => ({
        url: "/sample",
        method: "DELETE",
        params: { sampleId },
      }),
      transformResponse: (response) => {
        cartUpdated(response.count);
        return response.notifications[0];
      },
      invalidatesTags: (result, error, { sampleId }) => [
        { type: "Samples", id: sampleId },
      ],
    }),
  })
});

export const {
  useGetCartSamplesQuery,
  useGetCartQuery,
  useCountQuery,
  useEmptyMutation,
  useRemoveProjectMutation,
  useRemoveSampleMutation,
} = cartApi;

const updateCart = (data) => {
  data.notifications.forEach((n) => notification[n.type](n));
  cartUpdated(data.count);
  return data.count;
};

/**
 * Add samples for a project to the cart.
 * @param {number} projectId Identifier for the project the samples are from.
 * @param {array} samples array of sample {ids } to add to cart.
 * @returns {Promise<{count: any}>}
 */
export const putSampleInCart = async (projectId, samples) => {
  const { data } = await axios.post(AJAX_URL, {
    projectId,
    sampleIds: samples.map((s) => s.id),
  });
  return updateCart(data);
};

/**
 * Get the current number of samples in the cart
 * @returns {Promise<{count: any}>}
 */
export const getCartCount = async () => {
  const { data: count } = await axios.get(`${AJAX_URL}/count`);
  cartUpdated(count);
  return count;
};
<commit_msg>Prettier updated this<commit_after>import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { notification } from "antd";
import axios from "axios";
import { cartUpdated } from "../../utilities/events-utilities";
import { setBaseUrl } from "../../utilities/url-utilities";

const AJAX_URL = setBaseUrl(`/ajax/cart`);

export const cartApi = createApi({
  reducerPath: `cartApi`,
  baseQuery: fetchBaseQuery({ baseUrl: AJAX_URL }),
  tagTypes: ["Samples", "CartCount"],
  endpoints: (build) => ({
    /*
   Get all the samples in the cart.  This is specially formatted for the create new project page
   where samples need to be broken down into locked and unlocked samples.
    */
    getCartSamples: build.query({
      query: () => ({ url: "/all-samples" }),
    }),
    count: build.query({
      query: () => ({ url: "/count" }),
      providesTags: ["CartCount"],
      transformResponse: (response) => {
        cartUpdated(response);
        return response;
      },
    }),
    getCart: build.query({
      query: () => ({
        url: "/samples",
      }),
      providesTags: (result) =>
        result
          ? [
              ...result.map(({ id }) => ({ type: "Samples", id })),
              { type: "Samples", id: "LIST" },
            ]
          : [{ type: "Samples", id: "LIST" }],
      transformResponse(response, meta) {
        return response
          .map((project) => {
            const { samples, ...p } = project;
            return project.samples.map((sample) => ({ ...sample, project: p }));
          })
          .flat();
      },
    }),
    empty: build.mutation({
      query: () => ({ url: "", method: "DELETE" }),
      invalidatesTags: () => ["CartCount", "Samples"],
      transformResponse: (response) => {
        cartUpdated(0);
        return response;
      },
    }),
    removeProject: build.mutation({
      query: ({ id }) => ({
        url: "/project",
        method: "DELETE",
        params: { id },
      }),
      invalidatesTags: [{ type: "Samples", id: "LIST" }, "CartCount"],
      transformResponse: (response) => {
        cartUpdated(response.count);
        return response.notifications[0];
      },
    }),
    removeSample: build.mutation({
      query: ({ sampleId }) => ({
        url: "/sample",
        method: "DELETE",
        params: { sampleId },
      }),
      transformResponse: (response) => {
        cartUpdated(response.count);
        return response.notifications[0];
      },
      invalidatesTags: (result, error, { sampleId }) => [
        { type: "Samples", id: sampleId },
      ],
    }),
  }),
});

export const {
  useGetCartSamplesQuery,
  useGetCartQuery,
  useCountQuery,
  useEmptyMutation,
  useRemoveProjectMutation,
  useRemoveSampleMutation,
} = cartApi;

const updateCart = (data) => {
  data.notifications.forEach((n) => notification[n.type](n));
  cartUpdated(data.count);
  return data.count;
};

/**
 * Add samples for a project to the cart.
 * @param {number} projectId Identifier for the project the samples are from.
 * @param {array} samples array of sample {ids } to add to cart.
 * @returns {Promise<{count: any}>}
 */
export const putSampleInCart = async (projectId, samples) => {
  const { data } = await axios.post(AJAX_URL, {
    projectId,
    sampleIds: samples.map((s) => s.id),
  });
  return updateCart(data);
};

/**
 * Get the current number of samples in the cart
 * @returns {Promise<{count: any}>}
 */
export const getCartCount = async () => {
  const { data: count } = await axios.get(`${AJAX_URL}/count`);
  cartUpdated(count);
  return count;
};
<|endoftext|>