<commit_before>/*global defineSuite*/
defineSuite([
        'Scene/Expression',
        'Core/Color'
    ], function(
        Expression,
        Color) {
    'use strict';

    function MockStyleEngine() {
    }

    MockStyleEngine.prototype.makeDirty = function() {
    };

    function MockFeature() {
        this._properties = {};
    }

    MockFeature.prototype.addProperty = function(name, value) {
        this._properties[name] = value;
    };

    MockFeature.prototype.getProperty = function(name) {
        return this._properties[name];
    };

    it('evaluates variable', function() {
        var feature = new MockFeature();
        feature.addProperty('height', 10);
        feature.addProperty('width', 5);

        var expression = new Expression(new MockStyleEngine(), '${height}');
        expect(expression.evaluate(feature)).toEqual(10);

        expression = new Expression(new MockStyleEngine(), '${height}/${width}');
        expect(expression.evaluate(feature)).toEqual(2);

        expression = new Expression(new MockStyleEngine(), '${depth}');
        expect(expression.evaluate(feature)).toEqual(undefined);
    });

    it('throws on invalid expressions', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), false);
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 3');
        }).toThrowDeveloperError();
    });

    it('throws on unknown characters', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), '#');
        }).toThrowDeveloperError();
    });

    it('throws on unmatched parenthesis', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), '((true)');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '(true))');
        }).toThrowDeveloperError();
    });

    it('throws on unknown identifiers', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), 'flse');
        }).toThrowDeveloperError();
    });

    it('throws on unknown function calls', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), 'unknown()');
        }).toThrowDeveloperError();
    });

    it('throws with unsupported operators', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), '~1');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '+1');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 | 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 & 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 == 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 != 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 << 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 >> 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 >>> 3');
        }).toThrowDeveloperError();
    });

    it('evaluates literal null', function() {
        var expression = new Expression(new MockStyleEngine(), 'null');
        expect(expression.evaluate(undefined)).toEqual(null);
    });

    it('evaluates literal boolean', function() {
        var expression = new Expression(new MockStyleEngine(), 'true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'false');
        expect(expression.evaluate(undefined)).toEqual(false);
    });

    it('evaluates literal number', function() {
        var expression = new Expression(new MockStyleEngine(), '1');
        expect(expression.evaluate(undefined)).toEqual(1);

        expression = new Expression(new MockStyleEngine(), '0');
        expect(expression.evaluate(undefined)).toEqual(0);
    });

    it('evaluates literal string', function() {
        var expression = new Expression(new MockStyleEngine(), '\'hello\'');
        expect(expression.evaluate(undefined)).toEqual('hello');
    });

    it('evaluates literal color', function() {
        var expression = new Expression(new MockStyleEngine(), 'color(\'#ffffff\')');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'color(\'#fff\')');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'color(\'white\')');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'rgb(255, 255, 255)');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'hsl(0, 0, 1)');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 0.5)');
        expect(expression.evaluate(undefined)).toEqual(new Color(1.0, 1.0, 1.0, 0.5));

        expression = new Expression(new MockStyleEngine(), 'hsla(0, 0, 1, 0.5)');
        expect(expression.evaluate(undefined)).toEqual(new Color(1.0, 1.0, 1.0, 0.5));
    });

    it('evaluates unary not', function() {
        var expression = new Expression(new MockStyleEngine(), '!true');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '!!true');
        expect(expression.evaluate(undefined)).toEqual(true);
    });

    it('evaluates unary negative', function() {
        var expression = new Expression(new MockStyleEngine(), '-5');
        expect(expression.evaluate(undefined)).toEqual(-5);

        expression = new Expression(new MockStyleEngine(), '-(-5)');
        expect(expression.evaluate(undefined)).toEqual(5);
    });

    it('evaluates binary addition', function() {
        var expression = new Expression(new MockStyleEngine(), '1 + 2');
        expect(expression.evaluate(undefined)).toEqual(3);

        expression = new Expression(new MockStyleEngine(), '1 + 2 + 3 + 4');
        expect(expression.evaluate(undefined)).toEqual(10);
    });

    it('evaluates binary subtraction', function() {
        var expression = new Expression(new MockStyleEngine(), '2 - 1');
        expect(expression.evaluate(undefined)).toEqual(1);

        expression = new Expression(new MockStyleEngine(), '4 - 3 - 2 - 1');
        expect(expression.evaluate(undefined)).toEqual(-2);
    });

    it('evaluates binary multiplication', function() {
        var expression = new Expression(new MockStyleEngine(), '1 * 2');
        expect(expression.evaluate(undefined)).toEqual(2);

        expression = new Expression(new MockStyleEngine(), '1 * 2 * 3 * 4');
        expect(expression.evaluate(undefined)).toEqual(24);
    });

    it('evaluates binary division', function() {
        var expression = new Expression(new MockStyleEngine(), '2 / 1');
        expect(expression.evaluate(undefined)).toEqual(2);

        expression = new Expression(new MockStyleEngine(), '1/2');
        expect(expression.evaluate(undefined)).toEqual(0.5);

        expression = new Expression(new MockStyleEngine(), '24 / -4 / 2');
        expect(expression.evaluate(undefined)).toEqual(-3);
    });

    it('evaluates binary modulus', function() {
        var expression = new Expression(new MockStyleEngine(), '2 % 1');
        expect(expression.evaluate(undefined)).toEqual(0);

        expression = new Expression(new MockStyleEngine(), '6 % 4 % 3');
        expect(expression.evaluate(undefined)).toEqual(2);
    });

    it('evaluates binary equals', function() {
        var expression = new Expression(new MockStyleEngine(), '\'hello\' === \'hello\'');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '1 === 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'false === true === false');
        expect(expression.evaluate(undefined)).toEqual(true);
    });

    it('evaluates binary not equals', function() {
        var expression = new Expression(new MockStyleEngine(), '\'hello\' !== \'hello\'');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '1 !== 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'false !== true !== false');
        expect(expression.evaluate(undefined)).toEqual(true);
    });

    it('evaluates binary less than', function() {
        var expression = new Expression(new MockStyleEngine(), '2 < 3');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 < 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '3 < 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'true < false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') < 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates binary less than or equals', function() {
        var expression = new Expression(new MockStyleEngine(), '2 <= 3');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 <= 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '3 <= 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'true <= false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') <= 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates binary greater than', function() {
        var expression = new Expression(new MockStyleEngine(), '2 > 3');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '2 > 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '3 > 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'true > false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') > 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates binary greater than or equals', function() {
        var expression = new Expression(new MockStyleEngine(), '2 >= 3');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '2 >= 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '3 >= 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'true >= false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') >= 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates logical and', function() {
        var expression = new Expression(new MockStyleEngine(), 'false && false');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'false && true');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'true && true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 && color(\'red\')');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates logical or', function() {
        var expression = new Expression(new MockStyleEngine(), 'false || false');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'false || true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'true || true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 || color(\'red\')');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates color operations', function() {
        var expression = new Expression(new MockStyleEngine(), 'rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)');
        expect(expression.evaluate(undefined)).toEqual(Color.MAGENTA);

        expression = new Expression(new MockStyleEngine(), 'rgba(0, 255, 255, 1.0) - rgba(0, 255, 0, 0)');
        expect(expression.evaluate(undefined)).toEqual(Color.BLUE);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) * rgba(255, 0, 0, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(Color.RED);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 0, 1.0) * 1.0');
        expect(expression.evaluate(undefined)).toEqual(Color.YELLOW);

        expression = new Expression(new MockStyleEngine(), '1 * rgba(255, 255, 0, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(Color.YELLOW);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) / rgba(255, 255, 255, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) / 2');
        expect(expression.evaluate(undefined)).toEqual(new Color(0.5, 0.5, 0.5, 0.5));

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) % rgba(255, 255, 255, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(new Color(0, 0, 0, 0));

        expression = new Expression(new MockStyleEngine(), 'color(\'green\') === color(\'green\')');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'color(\'green\') !== color(\'green\')');
        expect(expression.evaluate(undefined)).toEqual(false);
    });
});
<commit_msg>Added tests for each type<commit_after>/*global defineSuite*/
defineSuite([
        'Scene/Expression',
        'Core/Color'
    ], function(
        Expression,
        Color) {
    'use strict';

    function MockStyleEngine() {
    }

    MockStyleEngine.prototype.makeDirty = function() {
    };

    function MockFeature() {
        this._properties = {};
    }

    MockFeature.prototype.addProperty = function(name, value) {
        this._properties[name] = value;
    };

    MockFeature.prototype.getProperty = function(name) {
        return this._properties[name];
    };

    it('evaluates variable', function() {
        var feature = new MockFeature();
        feature.addProperty('height', 10);
        feature.addProperty('width', 5);
        feature.addProperty('string', 'hello');
        feature.addProperty('boolean', true);
        feature.addProperty('null', null);
        feature.addProperty('undefined', undefined);

        var expression = new Expression(new MockStyleEngine(), '${height}');
        expect(expression.evaluate(feature)).toEqual(10);

        expression = new Expression(new MockStyleEngine(), '${height}/${width}');
        expect(expression.evaluate(feature)).toEqual(2);

        expression = new Expression(new MockStyleEngine(), '${string}');
        expect(expression.evaluate(feature)).toEqual('hello');

        expression = new Expression(new MockStyleEngine(), '\'replace ${string}\'');
        expect(expression.evaluate(feature)).toEqual('replace hello');

        expression = new Expression(new MockStyleEngine(), '${boolean}');
        expect(expression.evaluate(feature)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '${null}');
        expect(expression.evaluate(feature)).toEqual(null);

        expression = new Expression(new MockStyleEngine(), '${undefined}');
        expect(expression.evaluate(feature)).toEqual(undefined);

        expect(function() {
            return new Expression(new MockStyleEngine(), '${height');
        }).toThrowDeveloperError();
    });

    it('throws on invalid expressions', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), false);
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 3');
        }).toThrowDeveloperError();
    });

    it('throws on unknown characters', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), '#');
        }).toThrowDeveloperError();
    });

    it('throws on unmatched parenthesis', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), '((true)');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '(true))');
        }).toThrowDeveloperError();
    });

    it('throws on unknown identifiers', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), 'flse');
        }).toThrowDeveloperError();
    });

    it('throws on unknown function calls', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), 'unknown()');
        }).toThrowDeveloperError();
    });

    it('throws with unsupported operators', function() {
        expect(function() {
            return new Expression(new MockStyleEngine(), '~1');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '+1');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 | 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 & 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 == 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 != 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 << 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 >> 3');
        }).toThrowDeveloperError();

        expect(function() {
            return new Expression(new MockStyleEngine(), '2 >>> 3');
        }).toThrowDeveloperError();
    });

    it('evaluates literal null', function() {
        var expression = new Expression(new MockStyleEngine(), 'null');
        expect(expression.evaluate(undefined)).toEqual(null);
    });

    it('evaluates literal boolean', function() {
        var expression = new Expression(new MockStyleEngine(), 'true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'false');
        expect(expression.evaluate(undefined)).toEqual(false);
    });

    it('evaluates literal number', function() {
        var expression = new Expression(new MockStyleEngine(), '1');
        expect(expression.evaluate(undefined)).toEqual(1);

        expression = new Expression(new MockStyleEngine(), '0');
        expect(expression.evaluate(undefined)).toEqual(0);
    });

    it('evaluates literal string', function() {
        var expression = new Expression(new MockStyleEngine(), '\'hello\'');
        expect(expression.evaluate(undefined)).toEqual('hello');
    });

    it('evaluates literal color', function() {
        var expression = new Expression(new MockStyleEngine(), 'color(\'#ffffff\')');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'color(\'#fff\')');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'color(\'white\')');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'rgb(255, 255, 255)');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'hsl(0, 0, 1)');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 0.5)');
        expect(expression.evaluate(undefined)).toEqual(new Color(1.0, 1.0, 1.0, 0.5));

        expression = new Expression(new MockStyleEngine(), 'hsla(0, 0, 1, 0.5)');
        expect(expression.evaluate(undefined)).toEqual(new Color(1.0, 1.0, 1.0, 0.5));
    });

    it('evaluates unary not', function() {
        var expression = new Expression(new MockStyleEngine(), '!true');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '!!true');
        expect(expression.evaluate(undefined)).toEqual(true);
    });

    it('evaluates unary negative', function() {
        var expression = new Expression(new MockStyleEngine(), '-5');
        expect(expression.evaluate(undefined)).toEqual(-5);

        expression = new Expression(new MockStyleEngine(), '-(-5)');
        expect(expression.evaluate(undefined)).toEqual(5);
    });

    it('evaluates binary addition', function() {
        var expression = new Expression(new MockStyleEngine(), '1 + 2');
        expect(expression.evaluate(undefined)).toEqual(3);

        expression = new Expression(new MockStyleEngine(), '1 + 2 + 3 + 4');
        expect(expression.evaluate(undefined)).toEqual(10);
    });

    it('evaluates binary subtraction', function() {
        var expression = new Expression(new MockStyleEngine(), '2 - 1');
        expect(expression.evaluate(undefined)).toEqual(1);

        expression = new Expression(new MockStyleEngine(), '4 - 3 - 2 - 1');
        expect(expression.evaluate(undefined)).toEqual(-2);
    });

    it('evaluates binary multiplication', function() {
        var expression = new Expression(new MockStyleEngine(), '1 * 2');
        expect(expression.evaluate(undefined)).toEqual(2);

        expression = new Expression(new MockStyleEngine(), '1 * 2 * 3 * 4');
        expect(expression.evaluate(undefined)).toEqual(24);
    });

    it('evaluates binary division', function() {
        var expression = new Expression(new MockStyleEngine(), '2 / 1');
        expect(expression.evaluate(undefined)).toEqual(2);

        expression = new Expression(new MockStyleEngine(), '1/2');
        expect(expression.evaluate(undefined)).toEqual(0.5);

        expression = new Expression(new MockStyleEngine(), '24 / -4 / 2');
        expect(expression.evaluate(undefined)).toEqual(-3);
    });

    it('evaluates binary modulus', function() {
        var expression = new Expression(new MockStyleEngine(), '2 % 1');
        expect(expression.evaluate(undefined)).toEqual(0);

        expression = new Expression(new MockStyleEngine(), '6 % 4 % 3');
        expect(expression.evaluate(undefined)).toEqual(2);
    });

    it('evaluates binary equals', function() {
        var expression = new Expression(new MockStyleEngine(), '\'hello\' === \'hello\'');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '1 === 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'false === true === false');
        expect(expression.evaluate(undefined)).toEqual(true);
    });

    it('evaluates binary not equals', function() {
        var expression = new Expression(new MockStyleEngine(), '\'hello\' !== \'hello\'');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '1 !== 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'false !== true !== false');
        expect(expression.evaluate(undefined)).toEqual(true);
    });

    it('evaluates binary less than', function() {
        var expression = new Expression(new MockStyleEngine(), '2 < 3');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 < 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '3 < 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'true < false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') < 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates binary less than or equals', function() {
        var expression = new Expression(new MockStyleEngine(), '2 <= 3');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 <= 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '3 <= 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'true <= false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') <= 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates binary greater than', function() {
        var expression = new Expression(new MockStyleEngine(), '2 > 3');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '2 > 2');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '3 > 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'true > false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') > 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates binary greater than or equals', function() {
        var expression = new Expression(new MockStyleEngine(), '2 >= 3');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), '2 >= 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '3 >= 2');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'true >= false');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();

        expression = new Expression(new MockStyleEngine(), 'color(\'blue\') >= 10');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates logical and', function() {
        var expression = new Expression(new MockStyleEngine(), 'false && false');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'false && true');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'true && true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 && color(\'red\')');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates logical or', function() {
        var expression = new Expression(new MockStyleEngine(), 'false || false');
        expect(expression.evaluate(undefined)).toEqual(false);

        expression = new Expression(new MockStyleEngine(), 'false || true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'true || true');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), '2 || color(\'red\')');
        expect(function() {
            expression.evaluate(undefined);
        }).toThrowDeveloperError();
    });

    it('evaluates color operations', function() {
        var expression = new Expression(new MockStyleEngine(), 'rgba(255, 0, 0, 0.5) + rgba(0, 0, 255, 0.5)');
        expect(expression.evaluate(undefined)).toEqual(Color.MAGENTA);

        expression = new Expression(new MockStyleEngine(), 'rgba(0, 255, 255, 1.0) - rgba(0, 255, 0, 0)');
        expect(expression.evaluate(undefined)).toEqual(Color.BLUE);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) * rgba(255, 0, 0, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(Color.RED);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 0, 1.0) * 1.0');
        expect(expression.evaluate(undefined)).toEqual(Color.YELLOW);

        expression = new Expression(new MockStyleEngine(), '1 * rgba(255, 255, 0, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(Color.YELLOW);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) / rgba(255, 255, 255, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(Color.WHITE);

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) / 2');
        expect(expression.evaluate(undefined)).toEqual(new Color(0.5, 0.5, 0.5, 0.5));

        expression = new Expression(new MockStyleEngine(), 'rgba(255, 255, 255, 1.0) % rgba(255, 255, 255, 1.0)');
        expect(expression.evaluate(undefined)).toEqual(new Color(0, 0, 0, 0));

        expression = new Expression(new MockStyleEngine(), 'color(\'green\') === color(\'green\')');
        expect(expression.evaluate(undefined)).toEqual(true);

        expression = new Expression(new MockStyleEngine(), 'color(\'green\') !== color(\'green\')');
        expect(expression.evaluate(undefined)).toEqual(false);
    });
});
<|endoftext|>


<commit_before>let version = 'v1-retrieving-cached-assets',

    // The files we want to cache
    whitelistURIs = [
        '/',
        'css/pure/pure-min.css',
        'css/pure/grids-responsive-min.css',
        'css/news.css',
        'css/components/network-indicator.css',
        'lib/news-helper.js',
        'lib/service-worker-helper.js',
        'lib/network-indicator.js',
        'app.js',
        'news.json',
        'img/kingsman-logo.png',
        'img/avatars/gavin.png'
    ];

console.info('Executing service worker for', version);

self.addEventListener('install', (event) => {
    let cachesAreWritten = function () {
        console.info('adding whitelist to cache');

        return caches
            .open(version)
            .then((cache) => cache.addAll(whitelistURIs));
    };

    console.info(version, 'installing');

    event.waitUntil(cachesAreWritten());
});

self.addEventListener('activate', (event) => {
    console.info(version, 'activating');
});

self.addEventListener('fetch', (event) => {
    console.info(version, 'requesting', event.request.url);

    if (!event.request.url.endsWith('news.json') &&
        !inWhitelist(event.request.url)
    ) {
        return;
    }

    // Since we are taking control of the request, we will have to provide a response.
    event.respondWith(fetchCachePriority(version, event.request));
});

////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////  HELPERS  //////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @param  {string} requestURI
 * @return {boolean} true if the requestURI ends with anything in the whitelist.
 */
function inWhitelist(requestURI) {
    return whitelistURIs.some((whitelistURI) => {
        return requestURI.endsWith(whitelistURI);
    });
}

/**
 * Fetches a request, caches the response and returns the original response
 *
 * @param  {string} cacheName
 * @param  {Request} request
 * @return {Promise} A Promise that resolves to a Response object.
 */
function fetchCachePriority(cacheName, request) {
    let requestClone = request.clone();

    return caches.match(request)
        .then((cachedResponse) => {
            if (cachedResponse) {
                return cachedResponse;
            }

            return fetchAndCache(cacheName, requestClone);
        });

}

function fetchAndCache(cacheName, request) {
    return fetch(request)
        .then((response) => cacheResponse(cacheName, request, response));
}

/**
 * @param {string}   cacheName
 * @param {Request}  request
 * @param {Response} response
 */
function cacheResponse(cacheName, request, response) {
    // We need to clone the response too, as we will use it more than once.
    let responseClone = response.clone();

    console.info('Caching', request.url, 'in', cacheName);

    caches.open(cacheName)
        .then((cache) => cache.put(request, responseClone));

    return response;
}
<commit_msg>helper doesn't exist<commit_after>let version = 'v1-retrieving-cached-assets',

    // The files we want to cache
    whitelistURIs = [
        '/',
        'css/pure/pure-min.css',
        'css/pure/grids-responsive-min.css',
        'css/news.css',
        'css/components/network-indicator.css',
        'lib/news-helper.js',
        'lib/network-indicator.js',
        'app.js',
        'news.json',
        'img/kingsman-logo.png',
        'img/avatars/gavin.png'
    ];

console.info('Executing service worker for', version);

self.addEventListener('install', (event) => {
    let cachesAreWritten = function () {
        console.info('adding whitelist to cache');

        return caches
            .open(version)
            .then((cache) => cache.addAll(whitelistURIs));
    };

    console.info(version, 'installing');

    event.waitUntil(cachesAreWritten());
});

self.addEventListener('activate', (event) => {
    console.info(version, 'activating');
});

self.addEventListener('fetch', (event) => {
    console.info(version, 'requesting', event.request.url);

    if (!event.request.url.endsWith('news.json') &&
        !inWhitelist(event.request.url)
    ) {
        return;
    }

    // Since we are taking control of the request, we will have to provide a response.
    event.respondWith(fetchCachePriority(version, event.request));
});

////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////  HELPERS  //////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @param  {string} requestURI
 * @return {boolean} true if the requestURI ends with anything in the whitelist.
 */
function inWhitelist(requestURI) {
    return whitelistURIs.some((whitelistURI) => {
        return requestURI.endsWith(whitelistURI);
    });
}

/**
 * Fetches a request, caches the response and returns the original response
 *
 * @param  {string} cacheName
 * @param  {Request} request
 * @return {Promise} A Promise that resolves to a Response object.
 */
function fetchCachePriority(cacheName, request) {
    let requestClone = request.clone();

    return caches.match(request)
        .then((cachedResponse) => {
            if (cachedResponse) {
                return cachedResponse;
            }

            return fetchAndCache(cacheName, requestClone);
        });

}

function fetchAndCache(cacheName, request) {
    return fetch(request)
        .then((response) => cacheResponse(cacheName, request, response));
}

/**
 * @param {string}   cacheName
 * @param {Request}  request
 * @param {Response} response
 */
function cacheResponse(cacheName, request, response) {
    // We need to clone the response too, as we will use it more than once.
    let responseClone = response.clone();

    console.info('Caching', request.url, 'in', cacheName);

    caches.open(cacheName)
        .then((cache) => cache.put(request, responseClone));

    return response;
}
<|endoftext|>


<commit_before>steal('can/util/can.js',function (can) {
	// Given a list of elements, check if they are in the dom, if they
	// are in the dom, trigger inserted on them.
	can.inserted = function(elems){
		// prevent mutations from changing the looping
		elems = can.makeArray(elems);
		var inDocument = false,
			checked = false,
			children;
		for ( var i = 0, elem; (elem = elems[i]) !== undefined; i++ ) {
			if( !inDocument ) {
				if( elem.getElementsByTagName ){
					if( can.has( can.$(document.body) , elem ).length ) {
						inDocument = true;
					} else {
						return;
					}
				} else {
					continue;
				}
			}

			if(inDocument && elem.getElementsByTagName){
				children = can.makeArray( elem.getElementsByTagName("*") );
				can.trigger(elem,"inserted",[],false);
				for ( var j = 0, child; (child = children[j]) !== undefined; j++ ) {
					// Trigger the destroyed event
					can.trigger(child,"inserted",[],false);
				}
			}
		}
	}

	can.appendChild = function(el, child){
		if(child.nodeType === 11){
			var children = can.makeArray(child.childNodes);
		} else {
			var children = [child]
		}
		el.appendChild(child);
		can.inserted(children)
	}
	can.insertBefore = function(el, child, ref){
		if(child.nodeType === 11){
			var children = can.makeArray(child.childNodes);
		} else {
			var children = [child];
		}
		el.insertBefore(child, ref);
		can.inserted(children)
	}

});
<commit_msg>Added feature check for document.contains for Android #686<commit_after>steal('can/util/can.js',function (can) {
	// Given a list of elements, check if they are in the dom, if they
	// are in the dom, trigger inserted on them.
	can.inserted = function(elems){
		// prevent mutations from changing the looping
		elems = can.makeArray(elems);
		var inDocument = false,
			checked = false,
			// Not all browsers implement document.contains (Android)
			doc = can.$(document.contains ? document : document.body),
			children;
		for ( var i = 0, elem; (elem = elems[i]) !== undefined; i++ ) {
			if( !inDocument ) {
				if( elem.getElementsByTagName ){
					if( can.has( doc , elem ).length ) {
						inDocument = true;
					} else {
						return;
					}
				} else {
					continue;
				}
			}

			if(inDocument && elem.getElementsByTagName){
				children = can.makeArray( elem.getElementsByTagName("*") );
				can.trigger(elem,"inserted",[],false);
				for ( var j = 0, child; (child = children[j]) !== undefined; j++ ) {
					// Trigger the destroyed event
					can.trigger(child,"inserted",[],false);
				}
			}
		}
	}

	can.appendChild = function(el, child){
		if(child.nodeType === 11){
			var children = can.makeArray(child.childNodes);
		} else {
			var children = [child]
		}
		el.appendChild(child);
		can.inserted(children)
	}
	can.insertBefore = function(el, child, ref){
		if(child.nodeType === 11){
			var children = can.makeArray(child.childNodes);
		} else {
			var children = [child];
		}
		el.insertBefore(child, ref);
		can.inserted(children)
	}

});
<|endoftext|>


<commit_before>'use strict';

App.factory('StampDuty', function() {
    var SECOND_HOME_TAX_THRESHOLD = 40000,
        SECOND_HOME_TAX_RATE = 3;

    var stampDuty = {
      propertyPrice : 0,
      buyerType: '',
      rates_no_FTB: [
        {
          threshold: 125000,
          rate: 0
        }, {
          threshold: 250000,
          rate: 2
        }, {
          threshold: 925000,
          rate: 5
        }, {
          threshold: 1500000,
          rate: 10
        }, {
          threshold: 100000000,
          rate: 12
        }
      ],
      rates_FTB: [
        {
          threshold: 300000,
          rate: 0
        }, {
          threshold: 500000,
          rate: 5
        }
      ],

      cost: function() {
        var totalTax = 0,
            remaining = this.propertyPrice,
            rates,
            $conditionalMessage = $('.stamp-duty__FTB_conditional');

        if (this.buyerType === 'isFTB') {
          if (this.propertyPrice <= 500000) {
            rates = this.rates_FTB;
            $conditionalMessage.removeClass('is-active');
          } else {
            rates = this.rates_no_FTB;
            $conditionalMessage.addClass('is-active');
          }
        } else {
          rates = this.rates_no_FTB;
          $conditionalMessage.removeClass('is-active');
        }

        for (var i = 0; i < rates.length; i++) {
          var rateObj = rates[i],
              previousRateObj = i > 0 ? rates[i - 1] : null,
              bandwidth = 0,
              remainingTaxable = 0,
              bandTaxable = 0;

          if (!previousRateObj) {
            bandwidth = rateObj.threshold;
          } else {
            bandwidth = rateObj.threshold - previousRateObj.threshold;
          }

          remainingTaxable = Math.min(rateObj.threshold, remaining);
          bandTaxable = Math.min(bandwidth, remainingTaxable);
          totalTax += (bandTaxable * rateObj.rate / 100);
          remaining -= bandwidth;

          if (remaining < 0) {
            break;
          }
        }

        if (this.buyerType === 'isSecondHome' && this.propertyPrice >= SECOND_HOME_TAX_THRESHOLD) {
          totalTax += this.propertyPrice * (SECOND_HOME_TAX_RATE / 100);
        }

        return totalTax;
      },

      totalPurchase : function() {
        return (this.propertyPrice + this.cost());
      },

      percentageTax : function() {
        return ((this.cost() / this.propertyPrice) * 100) || 0;
      }
    };

    return stampDuty;
  });
<commit_msg>Adds FTB threshold as variable<commit_after>'use strict';

App.factory('StampDuty', function() {
    var SECOND_HOME_TAX_THRESHOLD = 40000,
        SECOND_HOME_TAX_RATE = 3,
        FIRST_TIME_BUYER_THRESHOLD = 500000;

    var stampDuty = {
      propertyPrice : 0,
      buyerType: '',
      rates_no_FTB: [
        {
          threshold: 125000,
          rate: 0
        }, {
          threshold: 250000,
          rate: 2
        }, {
          threshold: 925000,
          rate: 5
        }, {
          threshold: 1500000,
          rate: 10
        }, {
          threshold: 100000000,
          rate: 12
        }
      ],
      rates_FTB: [
        {
          threshold: 300000,
          rate: 0
        }, {
          threshold: 500000,
          rate: 5
        }
      ],

      cost: function() {
        var totalTax = 0,
            remaining = this.propertyPrice,
            rates,
            $conditionalMessage = $('.stamp-duty__FTB_conditional');

        if (this.buyerType === 'isFTB') {
          if (this.propertyPrice <= FIRST_TIME_BUYER_THRESHOLD) {
            rates = this.rates_FTB;
            $conditionalMessage.removeClass('is-active');
          } else {
            rates = this.rates_no_FTB;
            $conditionalMessage.addClass('is-active');
          }
        } else {
          rates = this.rates_no_FTB;
          $conditionalMessage.removeClass('is-active');
        }

        for (var i = 0; i < rates.length; i++) {
          var rateObj = rates[i],
              previousRateObj = i > 0 ? rates[i - 1] : null,
              bandwidth = 0,
              remainingTaxable = 0,
              bandTaxable = 0;

          if (!previousRateObj) {
            bandwidth = rateObj.threshold;
          } else {
            bandwidth = rateObj.threshold - previousRateObj.threshold;
          }

          remainingTaxable = Math.min(rateObj.threshold, remaining);
          bandTaxable = Math.min(bandwidth, remainingTaxable);
          totalTax += (bandTaxable * rateObj.rate / 100);
          remaining -= bandwidth;

          if (remaining < 0) {
            break;
          }
        }

        if (this.buyerType === 'isSecondHome' && this.propertyPrice >= SECOND_HOME_TAX_THRESHOLD) {
          totalTax += this.propertyPrice * (SECOND_HOME_TAX_RATE / 100);
        }

        return totalTax;
      },

      totalPurchase : function() {
        return (this.propertyPrice + this.cost());
      },

      percentageTax : function() {
        return ((this.cost() / this.propertyPrice) * 100) || 0;
      }
    };

    return stampDuty;
  });
<|endoftext|>


<commit_before>var $ = require('jquery');

var FileEditor = require('js/pages/FileEditor.js');

var activeUsers = [];
var collaborative = (typeof WebSocket !== 'undefined' && typeof sharejs !== 'undefined');

var ShareJSDoc = function(url, metadata, viewText, editor, renderer) {
    var self = this;
    self.editor = editor;
    self.renderer = renderer;
    var fileEditor = new FileEditor(url, viewText, self.editor, self.renderer);
    self.fileEditor = fileEditor;

    var viewModel = fileEditor.viewModel;
    var ctx = window.contextVars.file;

    // Initialize Ace and configure settings
    self.editor.getSession().setMode('ace/mode/markdown');
    self.editor.getSession().setUseSoftTabs(true);   // Replace tabs with spaces
    self.editor.getSession().setUseWrapMode(true);   // Wraps text
    self.editor.renderer.setShowGutter(false);       // Hides line number
    self.editor.setShowPrintMargin(false);           // Hides print margin
    self.editor.commands.removeCommand('showSettingsMenu');  // Disable settings menu
    self.editor.setReadOnly(true); // Read only until initialized
    self.editor.setOptions({
        enableBasicAutocompletion: false,
        enableSnippets: false,
        enableLiveAutocompletion: false
    });

    if (!collaborative) {
        // Populate editor with most recent draft
        viewModel.fetchData().done(function(response) {
            var content = response;
            self.editor.setValue(content, -1);
            self.editor.setReadOnly(false);
            if (typeof WebSocket === 'undefined') {
                viewModel.status('unsupported');
            } else {
                viewModel.status('disconnected');
            }
        });
        return;
    }

    // Requirements load order is specific in this case to compensate
    // for older browsers.
    var ReconnectingWebSocket = require('reconnectingWebsocket');
    require('addons/wiki/static/ace.js');

    // Configure connection
    var wsPrefix = (window.location.protocol === 'https:') ? 'wss://' : 'ws://';
    var wsUrl = wsPrefix + ctx.urls.sharejs;
    var socket = new ReconnectingWebSocket(wsUrl);
    var sjs = new sharejs.Connection(socket);
    var doc = sjs.get('docs', metadata.docId);
    var madeConnection = false;
    var allowRefresh = true;
    var refreshTriggered = false;
    var canEdit = true;

    function whenReady() {

        // Create a text document if one does not exist
        if (!doc.type) {
            doc.create('text');
        }

        viewModel.fetchData().done(function(response) {
            doc.attachAce(self.editor);
            if (viewModel.filesDiffer(viewModel.currentText(), response)) {
                viewModel.currentText(response);
            }
            unlockEditor();
            viewModel.status('connected');
            madeConnection = true;
        });

    }

    function unlockEditor() {
        self.editor.gotoLine(0,0);
        var undoManager = self.editor.getSession().getUndoManager();
        undoManager.reset();
        self.editor.getSession().setUndoManager(undoManager);
        self.editor.setReadOnly(false);
    }

    // Send user metadata
    function register() {
        socket.send(JSON.stringify(metadata));
    }

    function refreshMaybe() {
        if (allowRefresh && refreshTriggered) {
            if (canEdit) {
                window.location.reload();
            } else {
                window.location.replace(ctx.urls.content);
            }
        }
    }

    // Handle our custom messages separately
    var onmessage = socket.onmessage;
    socket.onmessage = function (message) {
        var data = JSON.parse(message.data);
        // Meta type is not built into sharejs; we pass it manually
        switch (data.type) {
            case 'meta':
                // Convert users object into knockout array
                activeUsers = [];
                for (var user in data.users) {
                    var userMeta = data.users[user];
                    userMeta.id = user;
                    activeUsers.push(userMeta);
                }
                viewModel.activeUsers(activeUsers);
                break;
            case 'lock':
                allowRefresh = false;
                self.editor.setReadOnly(true);
                permissionsModal.modal({
                    backdrop: 'static',
                    keyboard: false
                });
                setTimeout(function() {
                    allowRefresh = true;
                    refreshMaybe();
                }, 3000);
                break;
            case 'unlock':
                canEdit = data.contributors.indexOf(metadata.userId) > -1;
                refreshTriggered = true;
                refreshMaybe();
                break;
            default:
                onmessage(message);
                break;
        }
    };

    // Update status when reconnecting
    var onclose = socket.onclose;
    socket.onclose = function (event) {
        onclose(event);
        viewModel.status('connecting');
    };

    var onopen = socket.onopen;
    socket.onopen = function(event) {
        onopen(event);
        if (madeConnection) {
            viewModel.status('connected');
        }
    };

    // This will be called on both connect and reconnect
    doc.on('subscribe', register);

    // This will be called when we have a live copy of the server's data.
    doc.whenReady(whenReady);

    // Subscribe to changes
    doc.subscribe();
};

module.exports = ShareJSDoc;
<commit_msg>Remove unecesarry logic<commit_after>var $ = require('jquery');

var activeUsers = [];
var collaborative = (typeof WebSocket !== 'undefined' && typeof sharejs !== 'undefined');

var ShareJSDoc = function(wsUrl, metadata, editor) {
    var self = this;
    self.editor = editor;
    self.renderer = renderer;

    var ctx = window.contextVars.file;

    // Initialize Ace and configure settings
    self.editor.getSession().setMode('ace/mode/markdown');
    self.editor.getSession().setUseSoftTabs(true);   // Replace tabs with spaces
    self.editor.getSession().setUseWrapMode(true);   // Wraps text
    self.editor.renderer.setShowGutter(false);       // Hides line number
    self.editor.setShowPrintMargin(false);           // Hides print margin
    self.editor.commands.removeCommand('showSettingsMenu');  // Disable settings menu
    self.editor.setReadOnly(true); // Read only until initialized
    self.editor.setOptions({
        enableBasicAutocompletion: false,
        enableSnippets: false,
        enableLiveAutocompletion: false
    });

    self.collaborative = collaborative;

    // if (!collaborative) {
    //     // Populate editor with most recent draft
    //     viewModel.fetchData().done(function(response) {
    //         var content = response;
    //         self.editor.setValue(content, -1);
    //         self.editor.setReadOnly(false);
    //         if (typeof WebSocket === 'undefined') {
    //             viewModel.status('unsupported');
    //         } else {
    //             viewModel.status('disconnected');
    //         }
    //     });
    //     return;
    // }

    // Requirements load order is specific in this case to compensate
    // for older browsers.
    var ReconnectingWebSocket = require('reconnectingWebsocket');
    require('addons/wiki/static/ace.js');

    // Configure connection
    var wsPrefix = (window.location.protocol === 'https:') ? 'wss://' : 'ws://';
    var socket = new ReconnectingWebSocket(wsUrl);
    var sjs = new sharejs.Connection(socket);
    var doc = sjs.get('docs', metadata.docId);
    var madeConnection = false;
    var allowRefresh = true;
    var refreshTriggered = false;
    var canEdit = true;

    function whenReady() {

        // Create a text document if one does not exist
        if (!doc.type) {
            doc.create('text');
        }

        viewModel.fetchData().done(function(response) {
            doc.attachAce(self.editor);
            if (viewModel.filesDiffer(viewModel.currentText(), response)) {
                viewModel.currentText(response);
            }
            unlockEditor();
            viewModel.status('connected');
            madeConnection = true;
        });

    }

    function unlockEditor() {
        self.editor.gotoLine(0,0);
        var undoManager = self.editor.getSession().getUndoManager();
        undoManager.reset();
        self.editor.getSession().setUndoManager(undoManager);
        self.editor.setReadOnly(false);
    }

    // Send user metadata
    function register() {
        socket.send(JSON.stringify(metadata));
    }

    function refreshMaybe() {
        if (allowRefresh && refreshTriggered) {
            window.location.reload();
        }
    }

    // Handle our custom messages separately
    var onmessage = socket.onmessage;
    socket.onmessage = function (message) {
        var data = JSON.parse(message.data);
        // Meta type is not built into sharejs; we pass it manually
        switch (data.type) {
            case 'meta':
                // Convert users object into knockout array
                activeUsers = [];
                for (var user in data.users) {
                    var userMeta = data.users[user];
                    userMeta.id = user;
                    activeUsers.push(userMeta);
                }
                viewModel.activeUsers(activeUsers);
                break;
            case 'lock':
                allowRefresh = false;
                self.editor.setReadOnly(true);
                permissionsModal.modal({
                    backdrop: 'static',
                    keyboard: false
                });
                setTimeout(function() {
                    allowRefresh = true;
                    refreshMaybe();
                }, 3000);
                break;
            case 'unlock':
                canEdit = data.contributors.indexOf(metadata.userId) > -1;
                refreshTriggered = true;
                refreshMaybe();
                break;
            default:
                onmessage(message);
                break;
        }
    };

    // Update status when reconnecting
    var onclose = socket.onclose;
    socket.onclose = function (event) {
        onclose(event);
        viewModel.status('connecting');
    };

    var onopen = socket.onopen;
    socket.onopen = function(event) {
        onopen(event);
        if (madeConnection) {
            viewModel.status('connected');
        }
    };

    // This will be called on both connect and reconnect
    doc.on('subscribe', register);

    // This will be called when we have a live copy of the server's data.
    doc.whenReady(whenReady);

    // Subscribe to changes
    doc.subscribe();
};

module.exports = ShareJSDoc;
<|endoftext|>


<commit_before>var files = require('./files.js');
import { readFile, sha1 } from "./watch.js";

// Set METEOR_WATCH_FORCE_POLLING environment variable to a truthy value to
// force the use of files.watchFile instead of pathwatcher.watch.
// Enabled on Mac and Linux and disabled on Windows by default.
var PATHWATCHER_ENABLED = !process.env.METEOR_WATCH_FORCE_POLLING;
if (process.platform === "win32") {
  PATHWATCHER_ENABLED = false;
}

var DEFAULT_POLLING_INTERVAL =
      ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000;

var NO_PATHWATCHER_POLLING_INTERVAL =
      ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 500;

// When a file changes, we wait for this long before actually recomputing
// its hash, though of course we compute it immediately if anyone asks for
// it in the meantime.
const REHASH_DELAY_MS = 500;

// This may seems like a long time to wait before actually closing the
// file watchers, but it's to our advantage if they survive restarts.
const WATCHER_CLEANUP_DELAY_MS = 30000;

var suggestedRaisingWatchLimit = false;

const watchers = Object.create(null);

function acquireWatcher(absPath, callback) {
  const entry = watchers[absPath] || (
    watchers[absPath] = startNewWatcher(absPath));

  // The size of the entry.callbacks Set also serves as a reference count
  // for this watcher.
  entry.callbacks.add(callback);

  return entry;
}

function startNewWatcher(absPath) {
  let lastPathwatcherEventTime = 0;
  const callbacks = new Set;
  let latestHash = hashOrNull(absPath);
  let requestRehashTimer = null;
  let watcherCleanupTimer = null;

  function fire(self, args) {
    requestRehash();
    callbacks.forEach(cb => cb.apply(self, args));
  }

  function requestRehash() {
    if (requestRehashTimer === null) {
      requestRehashTimer = setTimeout(ensureLatestHash, REHASH_DELAY_MS);
    }
  }

  function ensureLatestHash() {
    if (requestRehashTimer !== null) {
      clearTimeout(requestRehashTimer);
      requestRehashTimer = null;
      latestHash = hashOrNull(absPath);
    }
    return latestHash;
  }

  function pathwatcherWrapper(...args) {
    lastPathwatcherEventTime = +new Date;
    fire(this, args);

    // It's tempting to call files.unwatchFile(absPath, watchFileWrapper)
    // here, but previous pathwatcher success is no guarantee of future
    // pathwatcher reliability. For example, pathwatcher works just fine
    // when file changes originate from within a Vagrant VM, but changes
    // to shared files made outside the VM are invisible to pathwatcher,
    // so our only hope of catching them is to continue polling.
  }

  const watcher = pathwatcherWatch(absPath, pathwatcherWrapper);

  const pollingInterval = watcher
    ? DEFAULT_POLLING_INTERVAL
    : NO_PATHWATCHER_POLLING_INTERVAL;

  function watchFileWrapper(...args) {
    const self = this;
    // If a pathwatcher event fired in the last polling interval, ignore
    // this event.
    if (new Date - lastPathwatcherEventTime > pollingInterval) {
      fire(this, args);
    }
  }

  // We use files.watchFile in addition to pathwatcher.watch as a
  // fail-safe to detect file changes even on network file systems.
  // However (unless the user disabled pathwatcher or this pathwatcher
  // call failed), we use a relatively long default polling interval of
  // 5000ms to save CPU cycles.
  files.watchFile(absPath, {
    persistent: false,
    interval: pollingInterval
  }, watchFileWrapper);

  return {
    callbacks,

    cheapHashOrNull: ensureLatestHash,

    release(callback) {
      if (! watchers[absPath]) {
        return;
      }

      callbacks.delete(callback);
      if (callbacks.size > 0) {
        return;
      }

      // Once there are no more callbacks in the Set, close both watchers
      // and nullify the shared data.
      clearTimeout(watcherCleanupTimer);
      watcherCleanupTimer = setTimeout(() => {
        if (callbacks.size > 0) {
          // If another callback was added while the timer was pending, we
          // can avoid tearing anything down.
          return;
        }

        watcher.close();
        files.unwatchFile(absPath, watchFileWrapper);
        watchers[absPath] = null;
      }, WATCHER_CLEANUP_DELAY_MS);
    }
  };
}

function pathwatcherWatch(absPath, callback) {
  if (PATHWATCHER_ENABLED) {
    try {
      return files.pathwatcherWatch(absPath, callback);
    } catch (e) {
      // If it isn't an actual pathwatcher failure, rethrow.
      if (e.message !== 'Unable to watch path') {
        throw e;
      }
      var constants = require('constants');
      var archinfo = require('../utils/archinfo.js');
      if (! suggestedRaisingWatchLimit &&
          // Note: the not-super-documented require('constants') maps from
          // strings to SYSTEM errno values. System errno values aren't the same
          // as the numbers used internally by libuv! Once we're upgraded
          // to Node 0.12, we'll have the system errno as a string (on 'code'),
          // but the support for that wasn't in Node 0.10's uv.
          // See our PR https://github.com/atom/node-pathwatcher/pull/53
          // (and make sure to read the final commit message, not the original
          // proposed PR, which had a slightly different interface).
          e.errno === constants.ENOSPC &&
          // The only suggestion we currently have is for Linux.
          archinfo.matches(archinfo.host(), 'os.linux')) {
        suggestedRaisingWatchLimit = true;
        var Console = require('../console/console.js').Console;
        Console.arrowWarn(
          "It looks like a simple tweak to your system's configuration will " +
            "make many tools (including this Meteor command) more efficient. " +
            "To learn more, see " +
            Console.url("https://github.com/meteor/meteor/wiki/File-Change-Watcher-Efficiency"));
      }
      // ... ignore the error.  We'll still have watchFile, which is good
      // enough.
    }
  }

  return null;
}

function hashOrNull(absPath) {
  const contents = readFile(absPath);
  return contents && sha1(contents) || null;
}

export function watch(absPath, callback) {
  const entry = acquireWatcher(absPath, callback);
  return {
    close() {
      entry.release(callback);
    }
  };
}

export function cheapHashOrNull(absPath) {
  const entry = watchers[absPath];
  return entry
    ? entry.cheapHashOrNull()
    : hashOrNull(absPath);
}
<commit_msg>Simplify rehashing in safe-pathwatcher.js.<commit_after>var files = require('./files.js');
import { readFile, sha1 } from "./watch.js";

// Set METEOR_WATCH_FORCE_POLLING environment variable to a truthy value to
// force the use of files.watchFile instead of pathwatcher.watch.
// Enabled on Mac and Linux and disabled on Windows by default.
var PATHWATCHER_ENABLED = !process.env.METEOR_WATCH_FORCE_POLLING;
if (process.platform === "win32") {
  PATHWATCHER_ENABLED = false;
}

var DEFAULT_POLLING_INTERVAL =
      ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000;

var NO_PATHWATCHER_POLLING_INTERVAL =
      ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 500;

// This may seems like a long time to wait before actually closing the
// file watchers, but it's to our advantage if they survive restarts.
const WATCHER_CLEANUP_DELAY_MS = 30000;

var suggestedRaisingWatchLimit = false;

const watchers = Object.create(null);

function acquireWatcher(absPath, callback) {
  const entry = watchers[absPath] || (
    watchers[absPath] = startNewWatcher(absPath));

  // The size of the entry.callbacks Set also serves as a reference count
  // for this watcher.
  entry.callbacks.add(callback);

  return entry;
}

function startNewWatcher(absPath) {
  let lastPathwatcherEventTime = 0;
  const callbacks = new Set;
  let latestHash = hashOrNull(absPath);
  let watcherCleanupTimer = null;

  function fire(self, args) {
    latestHash = void 0;
    callbacks.forEach(cb => cb.apply(self, args));
  }

  function ensureLatestHash() {
    if (typeof latestHash === "undefined") {
      latestHash = hashOrNull(absPath);
    }
    return latestHash;
  }

  function pathwatcherWrapper(...args) {
    lastPathwatcherEventTime = +new Date;
    fire(this, args);

    // It's tempting to call files.unwatchFile(absPath, watchFileWrapper)
    // here, but previous pathwatcher success is no guarantee of future
    // pathwatcher reliability. For example, pathwatcher works just fine
    // when file changes originate from within a Vagrant VM, but changes
    // to shared files made outside the VM are invisible to pathwatcher,
    // so our only hope of catching them is to continue polling.
  }

  const watcher = pathwatcherWatch(absPath, pathwatcherWrapper);

  const pollingInterval = watcher
    ? DEFAULT_POLLING_INTERVAL
    : NO_PATHWATCHER_POLLING_INTERVAL;

  function watchFileWrapper(...args) {
    const self = this;
    // If a pathwatcher event fired in the last polling interval, ignore
    // this event.
    if (new Date - lastPathwatcherEventTime > pollingInterval) {
      fire(this, args);
    }
  }

  // We use files.watchFile in addition to pathwatcher.watch as a
  // fail-safe to detect file changes even on network file systems.
  // However (unless the user disabled pathwatcher or this pathwatcher
  // call failed), we use a relatively long default polling interval of
  // 5000ms to save CPU cycles.
  files.watchFile(absPath, {
    persistent: false,
    interval: pollingInterval
  }, watchFileWrapper);

  return {
    callbacks,

    cheapHashOrNull: ensureLatestHash,

    release(callback) {
      if (! watchers[absPath]) {
        return;
      }

      callbacks.delete(callback);
      if (callbacks.size > 0) {
        return;
      }

      // Once there are no more callbacks in the Set, close both watchers
      // and nullify the shared data.
      clearTimeout(watcherCleanupTimer);
      watcherCleanupTimer = setTimeout(() => {
        if (callbacks.size > 0) {
          // If another callback was added while the timer was pending, we
          // can avoid tearing anything down.
          return;
        }

        watcher.close();
        files.unwatchFile(absPath, watchFileWrapper);
        watchers[absPath] = null;
      }, WATCHER_CLEANUP_DELAY_MS);
    }
  };
}

function pathwatcherWatch(absPath, callback) {
  if (PATHWATCHER_ENABLED) {
    try {
      return files.pathwatcherWatch(absPath, callback);
    } catch (e) {
      // If it isn't an actual pathwatcher failure, rethrow.
      if (e.message !== 'Unable to watch path') {
        throw e;
      }
      var constants = require('constants');
      var archinfo = require('../utils/archinfo.js');
      if (! suggestedRaisingWatchLimit &&
          // Note: the not-super-documented require('constants') maps from
          // strings to SYSTEM errno values. System errno values aren't the same
          // as the numbers used internally by libuv! Once we're upgraded
          // to Node 0.12, we'll have the system errno as a string (on 'code'),
          // but the support for that wasn't in Node 0.10's uv.
          // See our PR https://github.com/atom/node-pathwatcher/pull/53
          // (and make sure to read the final commit message, not the original
          // proposed PR, which had a slightly different interface).
          e.errno === constants.ENOSPC &&
          // The only suggestion we currently have is for Linux.
          archinfo.matches(archinfo.host(), 'os.linux')) {
        suggestedRaisingWatchLimit = true;
        var Console = require('../console/console.js').Console;
        Console.arrowWarn(
          "It looks like a simple tweak to your system's configuration will " +
            "make many tools (including this Meteor command) more efficient. " +
            "To learn more, see " +
            Console.url("https://github.com/meteor/meteor/wiki/File-Change-Watcher-Efficiency"));
      }
      // ... ignore the error.  We'll still have watchFile, which is good
      // enough.
    }
  }

  return null;
}

function hashOrNull(absPath) {
  const contents = readFile(absPath);
  return contents && sha1(contents) || null;
}

export function watch(absPath, callback) {
  const entry = acquireWatcher(absPath, callback);
  return {
    close() {
      entry.release(callback);
    }
  };
}

export function cheapHashOrNull(absPath) {
  const entry = watchers[absPath];
  return entry
    ? entry.cheapHashOrNull()
    : hashOrNull(absPath);
}
<|endoftext|>


<commit_before>'use strict';

let _got_body = {};
const mockGot = () => {
  if (_got_body === null)
    return Promise.reject();
  return Promise.resolve({
    body: _got_body
  });
};

jest.unmock('../search-client');
jest.unmock('../util');
jest.mock('got', () => mockGot);

const searchClient = require('../search-client');

function makeBackendContentsWith(packages, apiVersion) {
  const _newArr = [];
  for (const pkg of packages) {
    const _wrap = {
      version: [pkg.version],
      name: [pkg.name],
      description: [pkg.desc],
      author: [pkg.author],
      modified: [pkg.modified],
      keywords: [apiVersion]
    };
    _newArr.push(_wrap);
  }
  return _newArr;
}

describe('search-client.js', () => {

  describe('findCompatiblePackages', () => {

    pit('should return compatible packages from the backend', () => {
      const apiVersion = 'hain0';
      const packages = [
        {
          version: '0.0.1',
          name: 'hain-plugin-test',
          author: 'tester',
          modified: '2016-04-16',
          desc: 'test desc'
        }
      ];

      _got_body = {
        results: makeBackendContentsWith(packages, apiVersion)
      };

      return searchClient.findCompatiblePackages('fakeBackend', [apiVersion])
             .then(retPackages => {
               expect(retPackages).toEqual(packages);
             });
    });

    pit('should reject if `got` has rejected', (done) => {
      _got_body = null; // to make mockGot reject promise

      return searchClient.findCompatiblePackages('fakeBackend', [])
             .then((ret) => {
               throw new Error('Promise should not be resolved');
             }, (err) => {
               // done
             });
    });

  });

});
<commit_msg>Fix test<commit_after>'use strict';

jest.unmock('../search-client');
jest.unmock('../util');

const mock_got = require('got');
const searchClient = require('../search-client');

function makeBackendContentsWith(packages, apiVersion) {
  const _newArr = [];
  for (const pkg of packages) {
    const _wrap = {
      version: [pkg.version],
      name: [pkg.name],
      description: [pkg.desc],
      author: [pkg.author],
      modified: [pkg.modified],
      keywords: [apiVersion]
    };
    _newArr.push(_wrap);
  }
  return _newArr;
}

describe('search-client.js', () => {

  describe('findCompatiblePackages', () => {

    pit('should return compatible packages from the backend', () => {
      const apiVersion = 'hain0';
      const packages = [
        {
          version: '0.0.1',
          name: 'hain-plugin-test',
          author: 'tester',
          modified: '2016-04-16',
          __modified: '2016-04-16',
          desc: 'test desc'
        }
      ];

      mock_got.mockReturnValueOnce(Promise.resolve({
        body: {
          results: makeBackendContentsWith(packages, apiVersion)
        }
      }));

      return searchClient.findCompatiblePackages('fakeBackend', [apiVersion])
             .then(retPackages => {
               expect(retPackages).toEqual(packages);
             });
    });

    pit('should reject if `got` has rejected', (done) => {
      mock_got.mockReturnValueOnce(Promise.resolve(null));

      return searchClient.findCompatiblePackages('fakeBackend', [])
             .then((ret) => {
               throw new Error('Promise should not be resolved');
             }, (err) => {
               // done
             });
    });

  });

});
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var Item = function(name, price) {
  return {
    unitPrice: function() {
      return price;
    }
  }
};

describe('super market', function() {
  describe('item', function() {
    it('items have a unit price', function() {
      var item = new Item('beans', 45);
      assert.equal(45, item.unitPrice());
    });
  });
});
<commit_msg>refactor test name<commit_after>"use strict";

var assert = require('assert');

var Item = function(name, price) {
  return {
    unitPrice: function() {
      return price;
    }
  }
};

describe('super market', function() {
  describe('item', function() {
    it('have a unit price', function() {
      var item = new Item('beans', 45);
      assert.equal(45, item.unitPrice());
    });
  });
});
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var Item = function(desc) {

  var items = [];

  this.description = desc;

  return this;
};

var description = function(desc) {
  return function(x) {
    return x.description === desc;
  };
};

var Basket = function() {

  var items = [];

  this.add = function(item) {
    items.push(item);
  };

  this.contents = function() {
    return items;
  };

  this.itemsMatching = function(x) {
    return items.filter(x);
  };

  return this;
};

var Checkout = function() {

  this.price = function(basket) {
    return 0;
  };

  return this;
};

var Unit = function(item,price) {

  this.applies = function(basket) {
    return true;
  };

  return this;
};

describe('shopping basket', function() {
  describe('item', function() {
    it('exists', function() {
      assert(new Item('description'));
    });

    it('has a description', function() {
      var item = new Item('banana');
      assert.equal('banana', item.description);
    });
  });

  describe('basket', function() {
    it('exists', function() {
      assert(new Basket());
    });

    it('items can be added', function() {
      var basket = new Basket();
      basket.add(new Item('Shoes'));

      assert.equal(1, basket.contents().length);
    });

    it('can be queried', function() {
      var basket = new Basket();

      var items = basket.itemsMatching(
        function(x) {
          return false;
        }
      );

      assert.equal(0, items.length);
    });

    it('returns results', function() {
      var basket = new Basket();
      var beans = new Item('beans');
      basket.add(beans);

      assert.deepEqual(
        [beans],
        basket.itemsMatching(description('beans'))
      );
    });
  });

  describe('checkout', function() {
    it('exists', function() {
      assert(new Checkout());
    });

    it('prices an empty basket at zero', function() {
      var checkout = new Checkout();
      assert.equal(0, checkout.price(new Basket()));
    });
  });

  describe('offers', function() {
    it('unit', function() {
      var beans = new Item('beans');
      var basket = new Basket();
      var unit = new Unit(beans, 50);

      basket.add(beans);

      assert(unit.applies(basket));
    });
  });
});
<commit_msg>why did I stop, because theres a half decent film on<commit_after>"use strict";

var assert = require('assert');

var Item = function(desc) {

  var items = [];

  this.description = desc;

  return this;
};

var description = function(desc) {
  return function(x) {
    return x.description === desc;
  };
};

var Basket = function() {

  var items = [];

  this.add = function(item) {
    items.push(item);
  };

  this.contents = function() {
    return items;
  };

  this.itemsMatching = function(x) {
    return items.filter(x);
  };

  return this;
};

var Checkout = function() {

  this.price = function(basket) {
    return 0;
  };

  return this;
};

var Unit = function(item,price) {

  this.applies = function(basket) {
    return true;
  };

  return this;
};

describe('shopping basket', function() {
  describe('item', function() {
    it('exists', function() {
      assert(new Item('description'));
    });

    it('has a description', function() {
      var item = new Item('banana');
      assert.equal('banana', item.description);
    });
  });

  describe('basket', function() {
    it('exists', function() {
      assert(new Basket());
    });

    it('items can be added', function() {
      var basket = new Basket();
      basket.add(new Item('Shoes'));

      assert.equal(1, basket.contents().length);
    });

    it('can be queried', function() {
      var basket = new Basket();

      var items = basket.itemsMatching(
        function(x) {
          return false;
        }
      );

      assert.equal(0, items.length);
    });

    it('returns results', function() {
      var basket = new Basket();
      var beans = new Item('beans');
      basket.add(beans);

      assert.deepEqual(
        [beans],
        basket.itemsMatching(description('beans'))
      );
    });
  });

  describe('checkout', function() {
    it('exists', function() {
      assert(new Checkout());
    });

    it('prices an empty basket at zero', function() {
      var checkout = new Checkout();
      assert.equal(0, checkout.price(new Basket()));
    });
  });

  describe('offers', function() {
    it('unit', function() {
      var beans = new Item('beans');
      var basket = new Basket();
      var unit = new Unit(beans, 50);

      basket.add(beans);

      assert(unit.applies(basket));
    });
  });
});

// LA Story is on.
<|endoftext|>


<commit_before>TC.Cfg.acceptedBrowserVersions = [
  {
    "name": "Chrome",
    "version": 63
  },
  {
    "name": "Firefox",
    "version": 57
  },
  {
    "name": "IE",
    "version": 11
  },
  {
    "name": "Edge",
    "version": 16
  },
  {
    "name": "Safari",
    "version": 11
  },
  {
    "name": "Mobile Safari",
    "version": 11
  },
  {
    "name": "Opera mini",
    "version": "all"
  }
];
<commit_msg>Update minimum recommended browser versions<commit_after>TC.Cfg.acceptedBrowserVersions = [
  {
    "name": "Chrome",
    "version": 68
  },
  {
    "name": "Firefox",
    "version": 60
  },
  {
    "name": "IE",
    "version": 11
  },
  {
    "name": "Edge",
    "version": 17
  },
  {
    "name": "Safari",
    "version": 11
  },
  {
    "name": "Mobile Safari",
    "version": 11
  },
  {
    "name": "Opera mini",
    "version": "all"
  }
];
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
 * MIT-licensed (http://opensource.org/licenses/MIT)
 */

/**
 * @fileoverview
 * Including this file will add several additional shapes to Dygraph.Circles
 * which can be passed to drawPointCallback.
 * See tests/custom-circles.html for usage.
 */

(function() {

/**
 * @param {!CanvasRenderingContext2D} ctx the canvas context
 * @param {number} sides the number of sides in the shape.
 * @param {number} radius the radius of the image.
 * @param {number} cx center x coordate
 * @param {number} cy center y coordinate
 * @param {number=} rotationRadians the shift of the initial angle, in radians.
 * @param {number=} delta the angle shift for each line. If missing, creates a
 *     regular polygon.
 */
var regularShape = function(
    ctx, sides, radius, cx, cy, rotationRadians, delta) {
  rotationRadians = rotationRadians || 0;
  delta = delta || Math.PI * 2 / sides;

  ctx.beginPath();
  var initialAngle = rotationRadians;
  var angle = initialAngle;

  var computeCoordinates = function() {
    var x = cx + (Math.sin(angle) * radius);
    var y = cy + (-Math.cos(angle) * radius);
    return [x, y];
  };

  var initialCoordinates = computeCoordinates();
  var x = initialCoordinates[0];
  var y = initialCoordinates[1];
  ctx.moveTo(x, y);

  for (var idx = 0; idx < sides; idx++) {
    angle = (idx == sides - 1) ? initialAngle : (angle + delta);
    var coords = computeCoordinates();
    ctx.lineTo(coords[0], coords[1]);
  }
  ctx.fill();
  ctx.stroke();
};

/**
 * TODO(danvk): be more specific on the return type.
 * @param {number} sides
 * @param {number=} rotationRadians
 * @param {number=} delta
 * @return {Function}
 * @private
 */
var shapeFunction = function(sides, rotationRadians, delta) {
  return function(g, name, ctx, cx, cy, color, radius) {
    ctx.strokeStyle = color;
    ctx.fillStyle = "white";
    regularShape(ctx, sides, radius, cx, cy, rotationRadians, delta);
  };
};

Dygraph.update(Dygraph.Circles, {
  TRIANGLE : shapeFunction(3),
  SQUARE : shapeFunction(4, Math.PI / 4),
  DIAMOND : shapeFunction(4),
  PENTAGON : shapeFunction(5),
  HEXAGON : shapeFunction(6),
  CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
    ctx.beginPath();
    ctx.strokeStyle = color;
    ctx.fillStyle = "white";
    ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
    ctx.fill();
    ctx.stroke();
  },
  STAR : shapeFunction(5, 0, 4 * Math.PI / 5),
  PLUS : function(g, name, ctx, cx, cy, color, radius) {
    ctx.strokeStyle = color;

    ctx.beginPath();
    ctx.moveTo(cx + radius, cy);
    ctx.lineTo(cx - radius, cy);
    ctx.closePath();
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(cx, cy + radius);
    ctx.lineTo(cx, cy - radius);
    ctx.closePath();
    ctx.stroke();
  },
  EX : function(g, name, ctx, cx, cy, color, radius) {
    ctx.strokeStyle = color;

    ctx.beginPath();
    ctx.moveTo(cx + radius, cy + radius);
    ctx.lineTo(cx - radius, cy - radius);
    ctx.closePath();
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(cx + radius, cy - radius);
    ctx.lineTo(cx - radius, cy + radius);
    ctx.closePath();
    ctx.stroke();
  }
});

});
<commit_msg>Execute custom shapes closure<commit_after>/**
 * @license
 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
 * MIT-licensed (http://opensource.org/licenses/MIT)
 */

/**
 * @fileoverview
 * Including this file will add several additional shapes to Dygraph.Circles
 * which can be passed to drawPointCallback.
 * See tests/custom-circles.html for usage.
 */

(function() {

/**
 * @param {!CanvasRenderingContext2D} ctx the canvas context
 * @param {number} sides the number of sides in the shape.
 * @param {number} radius the radius of the image.
 * @param {number} cx center x coordate
 * @param {number} cy center y coordinate
 * @param {number=} rotationRadians the shift of the initial angle, in radians.
 * @param {number=} delta the angle shift for each line. If missing, creates a
 *     regular polygon.
 */
var regularShape = function(
    ctx, sides, radius, cx, cy, rotationRadians, delta) {
  rotationRadians = rotationRadians || 0;
  delta = delta || Math.PI * 2 / sides;

  ctx.beginPath();
  var initialAngle = rotationRadians;
  var angle = initialAngle;

  var computeCoordinates = function() {
    var x = cx + (Math.sin(angle) * radius);
    var y = cy + (-Math.cos(angle) * radius);
    return [x, y];
  };

  var initialCoordinates = computeCoordinates();
  var x = initialCoordinates[0];
  var y = initialCoordinates[1];
  ctx.moveTo(x, y);

  for (var idx = 0; idx < sides; idx++) {
    angle = (idx == sides - 1) ? initialAngle : (angle + delta);
    var coords = computeCoordinates();
    ctx.lineTo(coords[0], coords[1]);
  }
  ctx.fill();
  ctx.stroke();
};

/**
 * TODO(danvk): be more specific on the return type.
 * @param {number} sides
 * @param {number=} rotationRadians
 * @param {number=} delta
 * @return {Function}
 * @private
 */
var shapeFunction = function(sides, rotationRadians, delta) {
  return function(g, name, ctx, cx, cy, color, radius) {
    ctx.strokeStyle = color;
    ctx.fillStyle = "white";
    regularShape(ctx, sides, radius, cx, cy, rotationRadians, delta);
  };
};

Dygraph.update(Dygraph.Circles, {
  TRIANGLE : shapeFunction(3),
  SQUARE : shapeFunction(4, Math.PI / 4),
  DIAMOND : shapeFunction(4),
  PENTAGON : shapeFunction(5),
  HEXAGON : shapeFunction(6),
  CIRCLE : function(g, name, ctx, cx, cy, color, radius) {
    ctx.beginPath();
    ctx.strokeStyle = color;
    ctx.fillStyle = "white";
    ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
    ctx.fill();
    ctx.stroke();
  },
  STAR : shapeFunction(5, 0, 4 * Math.PI / 5),
  PLUS : function(g, name, ctx, cx, cy, color, radius) {
    ctx.strokeStyle = color;

    ctx.beginPath();
    ctx.moveTo(cx + radius, cy);
    ctx.lineTo(cx - radius, cy);
    ctx.closePath();
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(cx, cy + radius);
    ctx.lineTo(cx, cy - radius);
    ctx.closePath();
    ctx.stroke();
  },
  EX : function(g, name, ctx, cx, cy, color, radius) {
    ctx.strokeStyle = color;

    ctx.beginPath();
    ctx.moveTo(cx + radius, cy + radius);
    ctx.lineTo(cx - radius, cy - radius);
    ctx.closePath();
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(cx + radius, cy - radius);
    ctx.lineTo(cx - radius, cy + radius);
    ctx.closePath();
    ctx.stroke();
  }
});

})();
<|endoftext|>