<commit_before>(function() {
    var expect, lib, lexer;

    if(typeof require != 'undefined') {
        expect = require('expect.js');
        lib = require('../src/lib');
        lexer = require('../src/lexer');
    }
    else {
        expect = window.expect;
        lib = nunjucks.require('lib');
        lexer = nunjucks.require('lexer');
    }

    function _hasTokens(ws, tokens, types) {
        for(var i=0; i<types.length; i++) {
            var type = types[i];
            var tok = tokens.nextToken();

            if(!ws) {
                while(tok && tok.type == lexer.TOKEN_WHITESPACE) {
                    tok = tokens.nextToken();
                }
            }

            if(lib.isArray(type)) {
                expect(tok.type).to.be(type[0]);
                expect(tok.value).to.be(type[1]);
            }
            else {
                expect(tok.type).to.be(type);
            }
        }
    }

    function hasTokens(tokens /*, types */) {
        return _hasTokens(false, tokens, lib.toArray(arguments).slice(1));
    }

    function hasTokensWithWS(tokens /*, types */) {
        return _hasTokens(true, tokens, lib.toArray(arguments).slice(1));
    }

    describe('lexer', function() {
        var tok, tmpl, tokens;

        it('should parse template data', function() {
            tok = lexer.lex('3').nextToken();
            expect(tok.type).to.be(lexer.TOKEN_DATA);
            expect(tok.value).to.be('3');

            tmpl = 'foo bar bizzle 3 [1,2] !@#$%^&*()<>?:"{}|';
            tok = lexer.lex(tmpl).nextToken();
            expect(tok.type).to.be(lexer.TOKEN_DATA);
            expect(tok.value).to.be(tmpl);
        });

        it('should keep track of whitespace', function() {
            tokens = lexer.lex('data {% 1 2\n   3  %} data');
            hasTokensWithWS(tokens,
                            lexer.TOKEN_DATA,
                            lexer.TOKEN_BLOCK_START,
                            [lexer.TOKEN_WHITESPACE, ' '],
                            lexer.TOKEN_INT,
                            [lexer.TOKEN_WHITESPACE, ' '],
                            lexer.TOKEN_INT,
                            [lexer.TOKEN_WHITESPACE, '\n   '],
                            lexer.TOKEN_INT,
                            [lexer.TOKEN_WHITESPACE, '  '],
                            lexer.TOKEN_BLOCK_END,
                            lexer.TOKEN_DATA);
        });

        it('should parse variable start and end', function() {
            tokens = lexer.lex('data {{ foo }} bar bizzle');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END,
                      lexer.TOKEN_DATA);
        });

        it('should treat the non-breaking space as valid whitespace', function() {
            tokens = lexer.lex('data {{\u00A0foo }} bar bizzle');
            tok = tokens.nextToken();
            tok = tokens.nextToken();
            tok = tokens.nextToken();
            expect(tok.type).to.be(lexer.TOKEN_SYMBOL);
            expect(tok.value).to.be("foo");
        });

        it('should parse block start and end', function() {
            tokens = lexer.lex('data {% foo %} bar bizzle');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_BLOCK_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_BLOCK_END,
                      lexer.TOKEN_DATA);
        });

        it('should parse basic types', function() {
            tokens = lexer.lex('{{ 3 4.5 true false foo "hello" \'boo\' r/regex/ }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_FLOAT,
                      lexer.TOKEN_BOOLEAN,
                      lexer.TOKEN_BOOLEAN,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_STRING,
                      lexer.TOKEN_STRING,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should parse function calls', function() {
            tokens = lexer.lex('{{ foo(bar) }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      [lexer.TOKEN_SYMBOL, 'foo'],
                      lexer.TOKEN_LEFT_PAREN,
                      [lexer.TOKEN_SYMBOL, 'bar'],
                      lexer.TOKEN_RIGHT_PAREN,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse groups', function() {
            tokens = lexer.lex('{{ (1, 2, 3) }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_LEFT_PAREN,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_RIGHT_PAREN,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse arrays', function() {
            tokens = lexer.lex('{{ [1, 2, 3] }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_LEFT_BRACKET,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_RIGHT_BRACKET,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse dicts', function() {
            tokens = lexer.lex('{{ {one:1, "two":2} }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_LEFT_CURLY,
                      [lexer.TOKEN_SYMBOL, 'one'],
                      lexer.TOKEN_COLON,
                      [lexer.TOKEN_INT, '1'],
                      lexer.TOKEN_COMMA,
                      [lexer.TOKEN_STRING, 'two'],
                      lexer.TOKEN_COLON,
                      [lexer.TOKEN_INT, '2'],
                      lexer.TOKEN_RIGHT_CURLY,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse blocks without whitespace', function() {
            tokens = lexer.lex('data{{hello}}{%if%}data');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      [lexer.TOKEN_SYMBOL, 'hello'],
                      lexer.TOKEN_VARIABLE_END,
                      lexer.TOKEN_BLOCK_START,
                      [lexer.TOKEN_SYMBOL, 'if'],
                      lexer.TOKEN_BLOCK_END,
                      lexer.TOKEN_DATA);
        });

        it('should parse filters', function() {
            hasTokens(lexer.lex('{{ foo|bar }}'),
                      lexer.TOKEN_VARIABLE_START,
                      [lexer.TOKEN_SYMBOL, 'foo'],
                      lexer.TOKEN_PIPE,
                      [lexer.TOKEN_SYMBOL, 'bar'],
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should parse operators', function() {
            hasTokens(lexer.lex('{{ 3+3-3*3/3 }}'),
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_VARIABLE_END);

            hasTokens(lexer.lex('{{ 3**4//5 }}'),
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_VARIABLE_END);

            hasTokens(lexer.lex('{{ 3 != 4 == 5 <= 6 >= 7 < 8 > 9 }}'),
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should parse comments', function() {
            tokens = lexer.lex('data data {# comment #} data');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_COMMENT,
                      lexer.TOKEN_DATA);
        }),

        it('should allow changing the variable start and end', function() {
            tokens = lexer.lex('data {= var =}', {variableStart: '{=', variableEnd: '=}'});
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should allow changing the block start and end', function() {
            tokens = lexer.lex('{= =}', {blockStart: '{=', blockEnd: '=}'});
            hasTokens(tokens,
                      lexer.TOKEN_BLOCK_START,
                      lexer.TOKEN_BLOCK_END);
        }),

        it('should allow changing the variable start and end', function() {
            tokens = lexer.lex('data {= var =}', {variableStart: '{=', variableEnd: '=}'});
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should allow changing the comment start and end', function() {
            tokens = lexer.lex('<!-- A comment! -->', {commentStart: '<!--', commentEnd: '-->'});
            hasTokens(tokens,
                      lexer.TOKEN_COMMENT);
        }),

        /**
         * Test that this bug is fixed: https://github.com/mozilla/nunjucks/issues/235
         */
        it('should have individual lexer tag settings for each environment', function() {
            tokens = lexer.lex('{=', {variableStart: '{='});
            hasTokens(tokens, lexer.TOKEN_VARIABLE_START);

            tokens = lexer.lex('{{');
            hasTokens(tokens, lexer.TOKEN_VARIABLE_START);

            tokens = lexer.lex('{{', {variableStart: '<<<'});
            hasTokens(tokens, lexer.TOKEN_DATA);

            tokens = lexer.lex('{{');
            hasTokens(tokens, lexer.TOKEN_VARIABLE_START);
        });

        it('should parse regular expressions', function() {
            tokens = lexer.lex('{{ r/basic regex [a-z]/ }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);

            // A more complex regex with escaped slashes.
            tokens = lexer.lex('{{ r/{a*b} \\/regex! [0-9]\\// }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);

            // This one has flags.
            tokens = lexer.lex('{{ r/^x/gim }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);

            // This one has a valid flag then an invalid flag.
            tokens = lexer.lex('{{ r/x$/iv }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END);
        });
    });
})();
<commit_msg>Fix tests for non-breaking whitespaces<commit_after>(function() {
    var expect, lib, lexer;

    if(typeof require != 'undefined') {
        expect = require('expect.js');
        lib = require('../src/lib');
        lexer = require('../src/lexer');
    }
    else {
        expect = window.expect;
        lib = nunjucks.require('lib');
        lexer = nunjucks.require('lexer');
    }

    function _hasTokens(ws, tokens, types) {
        for(var i=0; i<types.length; i++) {
            var type = types[i];
            var tok = tokens.nextToken();

            if(!ws) {
                while(tok && tok.type == lexer.TOKEN_WHITESPACE) {
                    tok = tokens.nextToken();
                }
            }

            if(lib.isArray(type)) {
                expect(tok.type).to.be(type[0]);
                expect(tok.value).to.be(type[1]);
            }
            else {
                expect(tok.type).to.be(type);
            }
        }
    }

    function hasTokens(tokens /*, types */) {
        return _hasTokens(false, tokens, lib.toArray(arguments).slice(1));
    }

    function hasTokensWithWS(tokens /*, types */) {
        return _hasTokens(true, tokens, lib.toArray(arguments).slice(1));
    }

    describe('lexer', function() {
        var tok, tmpl, tokens;

        it('should parse template data', function() {
            tok = lexer.lex('3').nextToken();
            expect(tok.type).to.be(lexer.TOKEN_DATA);
            expect(tok.value).to.be('3');

            tmpl = 'foo bar bizzle 3 [1,2] !@#$%^&*()<>?:"{}|';
            tok = lexer.lex(tmpl).nextToken();
            expect(tok.type).to.be(lexer.TOKEN_DATA);
            expect(tok.value).to.be(tmpl);
        });

        it('should keep track of whitespace', function() {
            tokens = lexer.lex('data {% 1 2\n   3  %} data');
            hasTokensWithWS(tokens,
                            lexer.TOKEN_DATA,
                            lexer.TOKEN_BLOCK_START,
                            [lexer.TOKEN_WHITESPACE, ' '],
                            lexer.TOKEN_INT,
                            [lexer.TOKEN_WHITESPACE, ' '],
                            lexer.TOKEN_INT,
                            [lexer.TOKEN_WHITESPACE, '\n   '],
                            lexer.TOKEN_INT,
                            [lexer.TOKEN_WHITESPACE, '  '],
                            lexer.TOKEN_BLOCK_END,
                            lexer.TOKEN_DATA);
        });

        it('should parse variable start and end', function() {
            tokens = lexer.lex('data {{ foo }} bar bizzle');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END,
                      lexer.TOKEN_DATA);
        });

        it('should treat the non-breaking space as valid whitespace', function() {
            tokens = lexer.lex('{{\u00A0foo }}');
            tok = tokens.nextToken();
            tok = tokens.nextToken();
            tok = tokens.nextToken();
            expect(tok.type).to.be(lexer.TOKEN_SYMBOL);
            expect(tok.value).to.be("foo");
        });

        it('should parse block start and end', function() {
            tokens = lexer.lex('data {% foo %} bar bizzle');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_BLOCK_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_BLOCK_END,
                      lexer.TOKEN_DATA);
        });

        it('should parse basic types', function() {
            tokens = lexer.lex('{{ 3 4.5 true false foo "hello" \'boo\' r/regex/ }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_FLOAT,
                      lexer.TOKEN_BOOLEAN,
                      lexer.TOKEN_BOOLEAN,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_STRING,
                      lexer.TOKEN_STRING,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should parse function calls', function() {
            tokens = lexer.lex('{{ foo(bar) }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      [lexer.TOKEN_SYMBOL, 'foo'],
                      lexer.TOKEN_LEFT_PAREN,
                      [lexer.TOKEN_SYMBOL, 'bar'],
                      lexer.TOKEN_RIGHT_PAREN,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse groups', function() {
            tokens = lexer.lex('{{ (1, 2, 3) }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_LEFT_PAREN,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_RIGHT_PAREN,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse arrays', function() {
            tokens = lexer.lex('{{ [1, 2, 3] }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_LEFT_BRACKET,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_COMMA,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_RIGHT_BRACKET,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse dicts', function() {
            tokens = lexer.lex('{{ {one:1, "two":2} }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_LEFT_CURLY,
                      [lexer.TOKEN_SYMBOL, 'one'],
                      lexer.TOKEN_COLON,
                      [lexer.TOKEN_INT, '1'],
                      lexer.TOKEN_COMMA,
                      [lexer.TOKEN_STRING, 'two'],
                      lexer.TOKEN_COLON,
                      [lexer.TOKEN_INT, '2'],
                      lexer.TOKEN_RIGHT_CURLY,
                      lexer.TOKEN_VARIABLE_END);
        });

        it('should parse blocks without whitespace', function() {
            tokens = lexer.lex('data{{hello}}{%if%}data');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      [lexer.TOKEN_SYMBOL, 'hello'],
                      lexer.TOKEN_VARIABLE_END,
                      lexer.TOKEN_BLOCK_START,
                      [lexer.TOKEN_SYMBOL, 'if'],
                      lexer.TOKEN_BLOCK_END,
                      lexer.TOKEN_DATA);
        });

        it('should parse filters', function() {
            hasTokens(lexer.lex('{{ foo|bar }}'),
                      lexer.TOKEN_VARIABLE_START,
                      [lexer.TOKEN_SYMBOL, 'foo'],
                      lexer.TOKEN_PIPE,
                      [lexer.TOKEN_SYMBOL, 'bar'],
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should parse operators', function() {
            hasTokens(lexer.lex('{{ 3+3-3*3/3 }}'),
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_VARIABLE_END);

            hasTokens(lexer.lex('{{ 3**4//5 }}'),
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_VARIABLE_END);

            hasTokens(lexer.lex('{{ 3 != 4 == 5 <= 6 >= 7 < 8 > 9 }}'),
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_OPERATOR,
                      lexer.TOKEN_INT,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should parse comments', function() {
            tokens = lexer.lex('data data {# comment #} data');
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_COMMENT,
                      lexer.TOKEN_DATA);
        }),

        it('should allow changing the variable start and end', function() {
            tokens = lexer.lex('data {= var =}', {variableStart: '{=', variableEnd: '=}'});
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should allow changing the block start and end', function() {
            tokens = lexer.lex('{= =}', {blockStart: '{=', blockEnd: '=}'});
            hasTokens(tokens,
                      lexer.TOKEN_BLOCK_START,
                      lexer.TOKEN_BLOCK_END);
        }),

        it('should allow changing the variable start and end', function() {
            tokens = lexer.lex('data {= var =}', {variableStart: '{=', variableEnd: '=}'});
            hasTokens(tokens,
                      lexer.TOKEN_DATA,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END);
        }),

        it('should allow changing the comment start and end', function() {
            tokens = lexer.lex('<!-- A comment! -->', {commentStart: '<!--', commentEnd: '-->'});
            hasTokens(tokens,
                      lexer.TOKEN_COMMENT);
        }),

        /**
         * Test that this bug is fixed: https://github.com/mozilla/nunjucks/issues/235
         */
        it('should have individual lexer tag settings for each environment', function() {
            tokens = lexer.lex('{=', {variableStart: '{='});
            hasTokens(tokens, lexer.TOKEN_VARIABLE_START);

            tokens = lexer.lex('{{');
            hasTokens(tokens, lexer.TOKEN_VARIABLE_START);

            tokens = lexer.lex('{{', {variableStart: '<<<'});
            hasTokens(tokens, lexer.TOKEN_DATA);

            tokens = lexer.lex('{{');
            hasTokens(tokens, lexer.TOKEN_VARIABLE_START);
        });

        it('should parse regular expressions', function() {
            tokens = lexer.lex('{{ r/basic regex [a-z]/ }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);

            // A more complex regex with escaped slashes.
            tokens = lexer.lex('{{ r/{a*b} \\/regex! [0-9]\\// }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);

            // This one has flags.
            tokens = lexer.lex('{{ r/^x/gim }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_VARIABLE_END);

            // This one has a valid flag then an invalid flag.
            tokens = lexer.lex('{{ r/x$/iv }}');
            hasTokens(tokens,
                      lexer.TOKEN_VARIABLE_START,
                      lexer.TOKEN_REGEX,
                      lexer.TOKEN_SYMBOL,
                      lexer.TOKEN_VARIABLE_END);
        });
    });
})();
<|endoftext|>


<commit_before>var Vec3 = require('vec3').Vec3
  , assert = require('assert')
  , math = require('../math')
  , conv = require('../conversions')

module.exports = inject;

var EPSILON = 0.000001
  , PI = Math.PI
  , PI_2 = Math.PI * 2
  , POSITION_UPDATE_INTERVAL_MS = 50
  , PHYSICS_INTERVAL_MS = 20

function inject(bot) {
  var physics = {
    maxGroundSpeed: 4.27, // according to the internet
    terminalVelocity: 20.0, // guess
    walkingAcceleration: 100.0, // seems good
    gravity: 27.0, // seems good
    groundFriction: 0.9, // seems good
    playerApothem: 0.32, // notch's client F3 says 0.30, but that caused spankings
    playerHeight: 1.74, // tested with a binary search
    jumpSpeed: 9.0, // seems good
    yawSpeed: 3.0, // seems good
    sprintSpeed: 1.3, // correct
  };

  var controlState = {
    forward: false,
    back: false,
    left: false,
    right: false,
    jump: false,
    sprint: false,
  };
  var jumpQueued = false;
  var lastSentYaw = null;
  var positionUpdateTimer = null;
  var doPhysicsTimer = null;
  var lastPositionSentTime = null;
  var lastPhyicsFrameTime = null;

  function doPhysics() {
    var now = new Date();
    var deltaSeconds = (now - lastPhyicsFrameTime) / 1000;
    lastPhyicsFrameTime = now;
    nextFrame(deltaSeconds);
  }

  function cleanup() {
    stopPhysics();
    stopPositionUpdates();
  }

  function stopPositionUpdates() {
    clearInterval(positionUpdateTimer);
    positionUpdateTimer = null;
  }

  function stopPhysics() {
    clearInterval(doPhysicsTimer);
    doPhysicsTimer = null;
  }

  function nextFrame(deltaSeconds) {
    if (deltaSeconds < EPSILON) return; // too fast
    var pos = bot.entity.position;
    var vel = bot.entity.velocity;

    // derive xy movement vector from controls
    var movementRight = 0;
    if (controlState.right) movementRight += 1;
    if (controlState.left) movementRight -= 1;
    var movementForward = 0;
    if (controlState.forward) movementForward += 1;
    if (controlState.back) movementForward -= 1;

    // acceleration is m/s/s
    var acceleration = new Vec3(0, 0, 0);
    if (movementForward || movementRight) {
      // input acceleration
      var rotationFromInput = Math.atan2(-movementRight, movementForward);
      var inputYaw = bot.entity.yaw + rotationFromInput;
      acceleration.x += physics.walkingAcceleration * -Math.sin(inputYaw);
      acceleration.z += physics.walkingAcceleration * -Math.cos(inputYaw);
      if (controlState.sprint) {
        acceleration.x *= physics.sprintSpeed;
        acceleration.z *= physics.sprintSpeed;
      }
    }

    // jumping
    if ((controlState.jump || jumpQueued) && bot.entity.onGround) {
      vel.y = physics.jumpSpeed;
    }
    jumpQueued = false;

    // gravity
    acceleration.y -= physics.gravity;

    var oldGroundSpeedSquared = calcGroundSpeedSquared();
    if (oldGroundSpeedSquared < EPSILON) {
      // stopped
      vel.x = 0;
      vel.z = 0;
    } else {
      // non-zero ground speed
      var oldGroundSpeed = Math.sqrt(oldGroundSpeedSquared);
      var groundFriction = physics.groundFriction * physics.walkingAcceleration;
      // less friction for air
      if (! bot.entity.onGround) groundFriction *= 0.05;
      // if friction would stop the motion, do it
      var maybeNewGroundFriction = oldGroundSpeed / deltaSeconds;
      groundFriction = groundFriction > maybeNewGroundFriction ?
        maybeNewGroundFriction : groundFriction;
      acceleration.x -= vel.x / oldGroundSpeed * groundFriction;
      acceleration.z -= vel.z / oldGroundSpeed * groundFriction;
    }

    // calculate new speed
    vel.add(acceleration.scaled(deltaSeconds))

    // limit speed
    var groundSpeedSquared = calcGroundSpeedSquared();
    if (groundSpeedSquared > physics.maxGroundSpeed * physics.maxGroundSpeed) {
      var groundSpeed = Math.sqrt(groundSpeedSquared);
      var correctionScale = physics.maxGroundSpeed / groundSpeed;
      vel.x *= correctionScale;
      vel.z *= correctionScale;
    }
    vel.y = math.clamp(-physics.terminalVelocity, vel.y, physics.terminalVelocity);

    // calculate new positions and resolve collisions
    var boundingBox = getBoundingBox();
    var boundingBoxMin, boundingBoxMax;
    if (vel.x !== 0) {
      pos.x += vel.x * deltaSeconds;
      var blockX = Math.floor(pos.x + math.sign(vel.x) * physics.playerApothem);
      boundingBoxMin = new Vec3(blockX, boundingBox.min.y, boundingBox.min.z);
      boundingBoxMax = new Vec3(blockX, boundingBox.max.y, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.x = blockX + (vel.x < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.x = 0;
        boundingBox = getBoundingBox();
      }
    }

    if (vel.z !== 0) {
      pos.z += vel.z * deltaSeconds;
      var blockZ = Math.floor(pos.z + math.sign(vel.z) * physics.playerApothem);
      boundingBoxMin = new Vec3(boundingBox.min.x, boundingBox.min.y, blockZ);
      boundingBoxMax = new Vec3(boundingBox.max.x, boundingBox.max.y, blockZ);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.z = blockZ + (vel.z < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.z = 0;
        boundingBox = getBoundingBox();
      }
    }

    bot.entity.onGround = false;
    if (vel.y !== 0) {
      pos.y += vel.y * deltaSeconds;
      var playerHalfHeight = physics.playerHeight / 2;
      var blockY = Math.floor(pos.y + playerHalfHeight + math.sign(vel.y) * playerHalfHeight);
      boundingBoxMin = new Vec3(boundingBox.min.x, blockY, boundingBox.min.z);
      boundingBoxMax = new Vec3(boundingBox.max.x, blockY, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.y = blockY + (vel.y < 0 ? 1 : -physics.playerHeight) * 1.001;
        bot.entity.onGround = vel.y < 0 ? true : bot.entity.onGround;
        vel.y = 0;
      }
    }

    bot.emit('move');
  }

  function collisionInRange(boundingBoxMin, boundingBoxMax) {
    var cursor = new Vec3(0, 0, 0);
    var block;
    for (cursor.x = boundingBoxMin.x; cursor.x <= boundingBoxMax.x; cursor.x++) {
      for (cursor.y = boundingBoxMin.y; cursor.y <= boundingBoxMax.y; cursor.y++) {
        for (cursor.z = boundingBoxMin.z; cursor.z <= boundingBoxMax.z; cursor.z++) {
          block = bot.blockAt(cursor);
          if (block && block.boundingBox === 'block') return true;
        }
      }
    }

    return false;
  }

  function calcGroundSpeedSquared() {
    var vel = bot.entity.velocity;
    return vel.x * vel.x + vel.y * vel.y;
  }

  function getBoundingBox() {
    var pos = bot.entity.position;
    return {
      min: new Vec3(
        pos.x - physics.playerApothem,
        pos.y,
        pos.z - physics.playerApothem
      ).floor(),
      max: new Vec3(
        pos.x + physics.playerApothem,
        pos.y + physics.playerHeight,
        pos.z + physics.playerApothem
      ).floor(),
    };
  }

  function sendPositionAndLook(entity) {
    // sends data, no logic
    var packet = {
      x: entity.position.x,
      y: entity.position.y,
      z: entity.position.z,
      stance: entity.position.y + entity.height,
      onGround: entity.onGround,
    };
    packet.yaw = conv.toNotchianYaw(entity.yaw);
    packet.pitch = conv.toNotchianPitch(entity.pitch);
    bot.client.write(0x0d, packet);
  }

  function sendPosition() {
    // increment the yaw in baby steps so that notchian clients (not the server) can keep up.
    var sentPosition = {
      yaw: bot.entity.yaw % PI_2,
      pitch: bot.entity.pitch,
      position: bot.entity.position,
      velocity: bot.entity.velocity,
      height: bot.entity.height,
      onGround: bot.entity.onGround,
    }
    var deltaYaw = sentPosition.yaw - lastSentYaw;
    deltaYaw = deltaYaw < 0 ?
      (deltaYaw < -PI ? deltaYaw + PI_2 : deltaYaw) :
      (deltaYaw >  PI ? deltaYaw - PI_2 : deltaYaw);
    var absDeltaYaw = Math.abs(deltaYaw);
    assert.ok(absDeltaYaw < PI + 0.001);

    var now = new Date();
    var deltaMs = now - lastPositionSentTime;
    lastPositionSentTime = now;
    var maxDeltaYaw = deltaMs / 1000 * physics.yawSpeed;
    deltaYaw = absDeltaYaw > maxDeltaYaw ? maxDeltaYaw * math.sign(deltaYaw) : deltaYaw;
    lastSentYaw = (lastSentYaw + deltaYaw) % PI_2;
    sentPosition.yaw = lastSentYaw;

    sendPositionAndLook(sentPosition);
  }

  bot.physics = physics;

  bot.setControlState = function(control, state) {
    assert.ok(control in controlState, "invalid control: " + control);
    if (controlState[control] === state) return;
    controlState[control] = state;
    if (control === 'jump' && state) {
      jumpQueued = true;
    } else if(control === 'sprint') {
      bot.client.write(0x13, {
        entityId: bot.entity.id,
        actionId: state ? 4 : 5,
      });
    }
  };

  bot.clearControlStates = function() {
    for (var control in controlState) {
      bot.setControlState(control, false);
    }
  };

  bot.look = function(yaw, pitch, force) {
    bot.entity.yaw = yaw;
    bot.entity.pitch = pitch;
    if (force) lastSentYaw = yaw;
    bot.emit("move")
  };

  bot.lookAt = function(point, force) {
    var delta = point.minus(bot.entity.position);
    var yaw = Math.atan2(-delta.x, -delta.z);
    var groundDistance = Math.sqrt(delta.x * delta.x + delta.z * delta.z);
    var pitch = Math.atan2(delta.y, groundDistance);
    bot.look(yaw, pitch, force);
  };

  // player position and look
  bot.client.on(0x0d, function(packet) {
    if (positionUpdateTimer == null) {
      // got first 0x0d. start the clocks
      bot.entity.yaw = conv.fromNotchianYaw(packet.yaw);
      bot.entity.pitch = conv.fromNotchianPitch(packet.pitch);
      positionUpdateTimer = setInterval(sendPosition, POSITION_UPDATE_INTERVAL_MS);
    }

    bot.entity.velocity.set(0, 0, 0);
    bot.entity.position.set(packet.x, packet.y, packet.z);
    bot.entity.height = packet.stance - bot.entity.position.y;
    bot.entity.onGround = packet.onGround;

    // apologize to the notchian server by echoing an identical position back
    sendPositionAndLook(bot.entity);

    if (doPhysicsTimer == null) {
      lastSentYaw = math.euclideanMod(bot.entity.yaw, PI_2);
      lastPositionSentTime = new Date();
      lastPhyicsFrameTime = new Date();
      doPhysicsTimer = setInterval(doPhysics, PHYSICS_INTERVAL_MS);
    }
  });

  bot.on('mount', stopPhysics);
  bot.on('respawn', stopPhysics);
  bot.on('end', cleanup);
}
<commit_msg>physics: fix bug: walking too slowly on Z axis<commit_after>var Vec3 = require('vec3').Vec3
  , assert = require('assert')
  , math = require('../math')
  , conv = require('../conversions')

module.exports = inject;

var EPSILON = 0.000001
  , PI = Math.PI
  , PI_2 = Math.PI * 2
  , POSITION_UPDATE_INTERVAL_MS = 50
  , PHYSICS_INTERVAL_MS = 20

function inject(bot) {
  var physics = {
    maxGroundSpeed: 4.27, // according to the internet
    terminalVelocity: 20.0, // guess
    walkingAcceleration: 100.0, // seems good
    gravity: 27.0, // seems good
    groundFriction: 0.9, // seems good
    playerApothem: 0.32, // notch's client F3 says 0.30, but that caused spankings
    playerHeight: 1.74, // tested with a binary search
    jumpSpeed: 9.0, // seems good
    yawSpeed: 3.0, // seems good
    sprintSpeed: 1.3, // correct
  };

  var controlState = {
    forward: false,
    back: false,
    left: false,
    right: false,
    jump: false,
    sprint: false,
  };
  var jumpQueued = false;
  var lastSentYaw = null;
  var positionUpdateTimer = null;
  var doPhysicsTimer = null;
  var lastPositionSentTime = null;
  var lastPhyicsFrameTime = null;

  function doPhysics() {
    var now = new Date();
    var deltaSeconds = (now - lastPhyicsFrameTime) / 1000;
    lastPhyicsFrameTime = now;
    nextFrame(deltaSeconds);
  }

  function cleanup() {
    stopPhysics();
    stopPositionUpdates();
  }

  function stopPositionUpdates() {
    clearInterval(positionUpdateTimer);
    positionUpdateTimer = null;
  }

  function stopPhysics() {
    clearInterval(doPhysicsTimer);
    doPhysicsTimer = null;
  }

  function nextFrame(deltaSeconds) {
    if (deltaSeconds < EPSILON) return; // too fast
    var pos = bot.entity.position;
    var vel = bot.entity.velocity;

    // derive xy movement vector from controls
    var movementRight = 0;
    if (controlState.right) movementRight += 1;
    if (controlState.left) movementRight -= 1;
    var movementForward = 0;
    if (controlState.forward) movementForward += 1;
    if (controlState.back) movementForward -= 1;

    // acceleration is m/s/s
    var acceleration = new Vec3(0, 0, 0);
    if (movementForward || movementRight) {
      // input acceleration
      var rotationFromInput = Math.atan2(-movementRight, movementForward);
      var inputYaw = bot.entity.yaw + rotationFromInput;
      acceleration.x += physics.walkingAcceleration * -Math.sin(inputYaw);
      acceleration.z += physics.walkingAcceleration * -Math.cos(inputYaw);
      if (controlState.sprint) {
        acceleration.x *= physics.sprintSpeed;
        acceleration.z *= physics.sprintSpeed;
      }
    }

    // jumping
    if ((controlState.jump || jumpQueued) && bot.entity.onGround) {
      vel.y = physics.jumpSpeed;
    }
    jumpQueued = false;

    // gravity
    acceleration.y -= physics.gravity;

    var oldGroundSpeedSquared = calcGroundSpeedSquared();
    if (oldGroundSpeedSquared < EPSILON) {
      // stopped
      vel.x = 0;
      vel.z = 0;
    } else {
      // non-zero ground speed
      var oldGroundSpeed = Math.sqrt(oldGroundSpeedSquared);
      var groundFriction = physics.groundFriction * physics.walkingAcceleration;
      // less friction for air
      if (! bot.entity.onGround) groundFriction *= 0.05;
      // if friction would stop the motion, do it
      var maybeNewGroundFriction = oldGroundSpeed / deltaSeconds;
      groundFriction = groundFriction > maybeNewGroundFriction ?
        maybeNewGroundFriction : groundFriction;
      acceleration.x -= vel.x / oldGroundSpeed * groundFriction;
      acceleration.z -= vel.z / oldGroundSpeed * groundFriction;
    }

    // calculate new speed
    vel.add(acceleration.scaled(deltaSeconds))

    // limit speed
    var groundSpeedSquared = calcGroundSpeedSquared();
    if (groundSpeedSquared > physics.maxGroundSpeed * physics.maxGroundSpeed) {
      var groundSpeed = Math.sqrt(groundSpeedSquared);
      var correctionScale = physics.maxGroundSpeed / groundSpeed;
      vel.x *= correctionScale;
      vel.z *= correctionScale;
    }
    vel.y = math.clamp(-physics.terminalVelocity, vel.y, physics.terminalVelocity);

    // calculate new positions and resolve collisions
    var boundingBox = getBoundingBox();
    var boundingBoxMin, boundingBoxMax;
    if (vel.x !== 0) {
      pos.x += vel.x * deltaSeconds;
      var blockX = Math.floor(pos.x + math.sign(vel.x) * physics.playerApothem);
      boundingBoxMin = new Vec3(blockX, boundingBox.min.y, boundingBox.min.z);
      boundingBoxMax = new Vec3(blockX, boundingBox.max.y, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.x = blockX + (vel.x < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.x = 0;
        boundingBox = getBoundingBox();
      }
    }

    if (vel.z !== 0) {
      pos.z += vel.z * deltaSeconds;
      var blockZ = Math.floor(pos.z + math.sign(vel.z) * physics.playerApothem);
      boundingBoxMin = new Vec3(boundingBox.min.x, boundingBox.min.y, blockZ);
      boundingBoxMax = new Vec3(boundingBox.max.x, boundingBox.max.y, blockZ);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.z = blockZ + (vel.z < 0 ? 1 + physics.playerApothem : -physics.playerApothem) * 1.001;
        vel.z = 0;
        boundingBox = getBoundingBox();
      }
    }

    bot.entity.onGround = false;
    if (vel.y !== 0) {
      pos.y += vel.y * deltaSeconds;
      var playerHalfHeight = physics.playerHeight / 2;
      var blockY = Math.floor(pos.y + playerHalfHeight + math.sign(vel.y) * playerHalfHeight);
      boundingBoxMin = new Vec3(boundingBox.min.x, blockY, boundingBox.min.z);
      boundingBoxMax = new Vec3(boundingBox.max.x, blockY, boundingBox.max.z);
      if (collisionInRange(boundingBoxMin, boundingBoxMax)) {
        pos.y = blockY + (vel.y < 0 ? 1 : -physics.playerHeight) * 1.001;
        bot.entity.onGround = vel.y < 0 ? true : bot.entity.onGround;
        vel.y = 0;
      }
    }

    bot.emit('move');
  }

  function collisionInRange(boundingBoxMin, boundingBoxMax) {
    var cursor = new Vec3(0, 0, 0);
    var block;
    for (cursor.x = boundingBoxMin.x; cursor.x <= boundingBoxMax.x; cursor.x++) {
      for (cursor.y = boundingBoxMin.y; cursor.y <= boundingBoxMax.y; cursor.y++) {
        for (cursor.z = boundingBoxMin.z; cursor.z <= boundingBoxMax.z; cursor.z++) {
          block = bot.blockAt(cursor);
          if (block && block.boundingBox === 'block') return true;
        }
      }
    }

    return false;
  }

  function calcGroundSpeedSquared() {
    var vel = bot.entity.velocity;
    return vel.x * vel.x + vel.z * vel.z;
  }

  function getBoundingBox() {
    var pos = bot.entity.position;
    return {
      min: new Vec3(
        pos.x - physics.playerApothem,
        pos.y,
        pos.z - physics.playerApothem
      ).floor(),
      max: new Vec3(
        pos.x + physics.playerApothem,
        pos.y + physics.playerHeight,
        pos.z + physics.playerApothem
      ).floor(),
    };
  }

  function sendPositionAndLook(entity) {
    // sends data, no logic
    var packet = {
      x: entity.position.x,
      y: entity.position.y,
      z: entity.position.z,
      stance: entity.position.y + entity.height,
      onGround: entity.onGround,
    };
    packet.yaw = conv.toNotchianYaw(entity.yaw);
    packet.pitch = conv.toNotchianPitch(entity.pitch);
    bot.client.write(0x0d, packet);
  }

  function sendPosition() {
    // increment the yaw in baby steps so that notchian clients (not the server) can keep up.
    var sentPosition = {
      yaw: bot.entity.yaw % PI_2,
      pitch: bot.entity.pitch,
      position: bot.entity.position,
      velocity: bot.entity.velocity,
      height: bot.entity.height,
      onGround: bot.entity.onGround,
    }
    var deltaYaw = sentPosition.yaw - lastSentYaw;
    deltaYaw = deltaYaw < 0 ?
      (deltaYaw < -PI ? deltaYaw + PI_2 : deltaYaw) :
      (deltaYaw >  PI ? deltaYaw - PI_2 : deltaYaw);
    var absDeltaYaw = Math.abs(deltaYaw);
    assert.ok(absDeltaYaw < PI + 0.001);

    var now = new Date();
    var deltaMs = now - lastPositionSentTime;
    lastPositionSentTime = now;
    var maxDeltaYaw = deltaMs / 1000 * physics.yawSpeed;
    deltaYaw = absDeltaYaw > maxDeltaYaw ? maxDeltaYaw * math.sign(deltaYaw) : deltaYaw;
    lastSentYaw = (lastSentYaw + deltaYaw) % PI_2;
    sentPosition.yaw = lastSentYaw;

    sendPositionAndLook(sentPosition);
  }

  bot.physics = physics;

  bot.setControlState = function(control, state) {
    assert.ok(control in controlState, "invalid control: " + control);
    if (controlState[control] === state) return;
    controlState[control] = state;
    if (control === 'jump' && state) {
      jumpQueued = true;
    } else if(control === 'sprint') {
      bot.client.write(0x13, {
        entityId: bot.entity.id,
        actionId: state ? 4 : 5,
      });
    }
  };

  bot.clearControlStates = function() {
    for (var control in controlState) {
      bot.setControlState(control, false);
    }
  };

  bot.look = function(yaw, pitch, force) {
    bot.entity.yaw = yaw;
    bot.entity.pitch = pitch;
    if (force) lastSentYaw = yaw;
    bot.emit("move")
  };

  bot.lookAt = function(point, force) {
    var delta = point.minus(bot.entity.position);
    var yaw = Math.atan2(-delta.x, -delta.z);
    var groundDistance = Math.sqrt(delta.x * delta.x + delta.z * delta.z);
    var pitch = Math.atan2(delta.y, groundDistance);
    bot.look(yaw, pitch, force);
  };

  // player position and look
  bot.client.on(0x0d, function(packet) {
    if (positionUpdateTimer == null) {
      // got first 0x0d. start the clocks
      bot.entity.yaw = conv.fromNotchianYaw(packet.yaw);
      bot.entity.pitch = conv.fromNotchianPitch(packet.pitch);
      positionUpdateTimer = setInterval(sendPosition, POSITION_UPDATE_INTERVAL_MS);
    }

    bot.entity.velocity.set(0, 0, 0);
    bot.entity.position.set(packet.x, packet.y, packet.z);
    bot.entity.height = packet.stance - bot.entity.position.y;
    bot.entity.onGround = packet.onGround;

    // apologize to the notchian server by echoing an identical position back
    sendPositionAndLook(bot.entity);

    if (doPhysicsTimer == null) {
      lastSentYaw = math.euclideanMod(bot.entity.yaw, PI_2);
      lastPositionSentTime = new Date();
      lastPhyicsFrameTime = new Date();
      doPhysicsTimer = setInterval(doPhysics, PHYSICS_INTERVAL_MS);
    }
  });

  bot.on('mount', stopPhysics);
  bot.on('respawn', stopPhysics);
  bot.on('end', cleanup);
}
<|endoftext|>


<commit_before>'use strict';

var levels = require('../levels');
var times = require('../times');
var lastChecked = new Date();
var lastSuspendTime = new Date("1900-01-01");

function init(ctx) {
  var translate = ctx.language.translate;

  var timeago = {
    name: 'timeago',
    label: 'Timeago',
    pluginType: 'pill-status',
    pillFlip: true
  };

  timeago.checkNotifications = function checkNotifications(sbx) {

    console.log('timeago.checkNotifications');

    if (!sbx.extendedSettings.enableAlerts) {
      return;
    }

    var lastSGVEntry = sbx.lastSGVEntry();

    if (!lastSGVEntry || lastSGVEntry.mills >= sbx.time) {
      return;
    }

    function buildMessage(agoDisplay) {
      var lines = sbx.prepareDefaultLines();
      lines.unshift(translate('Last received:') + ' ' + [agoDisplay.value, agoDisplay.label].join(' '));
      return lines.join('\n');
    }

    function sendAlarm(opts) {
      var agoDisplay = timeago.calcDisplay(lastSGVEntry, sbx.time);

      sbx.notifications.requestNotify({
        level: opts.level,
        title: translate('Stale data, check rig?'),
        message: buildMessage(agoDisplay),
        eventName: timeago.name,
        plugin: timeago,
        group: 'Time Ago',
        pushoverSound: opts.pushoverSound,
        debug: agoDisplay
      });
    }

    var status = timeago.checkStatus(sbx);
    if (status === 'urgent') {
      sendAlarm({
        level: levels.URGENT,
        pushoverSound: 'echo'
      });
    } else if (status === 'warn') {
      sendAlarm({
        level: levels.WARN,
        pushoverSound: 'echo'
      });
    }

  };

  timeago.checkStatus = function checkStatus(sbx) {

    // Check if the app has been suspended; if yes, snooze data missing alarmn for 15 seconds
    var now = new Date();
    var delta = now.getTime() - lastChecked.getTime();
    lastChecked = now;

    if (delta > 15 * 1000) { // Looks like we've been hibernating
      lastSuspendTime = now;
    }

    var timeSinceLastSuspended = now.getTime() - lastSuspendTime.getTime();

    if (timeSinceLastSuspended < (15 * 10000)) {
      console.log('Hibernation detected, suspending timeago alarm');
      return 'current';
    }

    var lastSGVEntry = sbx.lastSGVEntry(),
      warn = sbx.settings.alarmTimeagoWarn,
      warnMins = sbx.settings.alarmTimeagoWarnMins || 15,
      urgent = sbx.settings.alarmTimeagoUrgent,
      urgentMins = sbx.settings.alarmTimeagoUrgentMins || 30;

    function isStale(mins) {
      return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs;
    }

    var status = 'current';

    if (!lastSGVEntry) {
      //assume current
    } else if (urgent && isStale(urgentMins)) {
      status = 'urgent';
    } else if (warn && isStale(warnMins)) {
      status = 'warn';
    }

    return status;

  };

  timeago.isMissing = function isMissing(opts) {
    if (!opts || !opts.entry || isNaN(opts.entry.mills) || isNaN(opts.time) || isNaN(opts.timeSince)) {
      return {
        label: translate('time ago'),
        shortLabel: translate('ago')
      };
    }
  };

  timeago.inTheFuture = function inTheFuture(opts) {
    if (opts.entry.mills - times.mins(5).msecs > opts.time) {
      return {
        label: translate('in the future'),
        shortLabel: translate('future')
      };
    }
  };

  timeago.almostInTheFuture = function almostInTheFuture(opts) {
    if (opts.entry.mills > opts.time) {
      return {
        value: 1,
        label: translate('min ago'),
        shortLabel: 'm'
      };
    }
  };

  timeago.isLessThan = function isLessThan(limit, divisor, label, shortLabel) {
    return function checkIsLessThan(opts) {
      if (opts.timeSince < limit) {
        return {
          value: Math.max(1, Math.round(opts.timeSince / divisor)),
          label: label,
          shortLabel: shortLabel
        };
      }
    };
  };

  timeago.resolvers = [
    timeago.isMissing, timeago.inTheFuture, timeago.almostInTheFuture, timeago.isLessThan(times.mins(2).msecs, times.min().msecs, 'min ago', 'm'), timeago.isLessThan(times.hour().msecs, times.min().msecs, 'mins ago', 'm'), timeago.isLessThan(times.hours(2).msecs, times.hour().msecs, 'hour ago', 'h'), timeago.isLessThan(times.day().msecs, times.hour().msecs, 'hours ago', 'h'), timeago.isLessThan(times.days(2).msecs, times.day().msecs, 'day ago', 'd'), timeago.isLessThan(times.week().msecs, times.day().msecs, 'days ago', 'd'),
    function () {
      return {
        label: 'long ago',
        shortLabel: 'ago'
      }
    }
  ];

  timeago.calcDisplay = function calcDisplay(entry, time) {
    var opts = {
      time: time,
      entry: entry
    };

    if (time && entry && entry.mills) {
      opts.timeSince = time - entry.mills;
    }

    for (var i = 0; i < timeago.resolvers.length; i++) {
      var value = timeago.resolvers[i](opts);
      if (value) {
        return value;
      }
    }
  };

  timeago.updateVisualisation = function updateVisualisation(sbx) {
    var agoDisplay = timeago.calcDisplay(sbx.lastSGVEntry(), sbx.time);
    var inRetroMode = sbx.data.inRetroMode;

    sbx.pluginBase.updatePillText(timeago, {
      value: inRetroMode ? null : agoDisplay.value,
      label: inRetroMode ? translate('RETRO') : translate(agoDisplay.label)
        //no warning/urgent class when in retro mode
        ,
      pillClass: inRetroMode ? 'current' : timeago.checkStatus(sbx)
    });
  };

  return timeago;

}

module.exports = init;<commit_msg>Wip/ios springboard app fixes (#4543)<commit_after>'use strict';

var levels = require('../levels');
var times = require('../times');
var lastChecked = new Date();
var lastSuspendTime = new Date("1900-01-01");

function init(ctx) {
  var translate = ctx.language.translate;

  var timeago = {
    name: 'timeago',
    label: 'Timeago',
    pluginType: 'pill-status',
    pillFlip: true
  };

  timeago.checkNotifications = function checkNotifications(sbx) {

    console.log('timeago.checkNotifications');

    if (!sbx.extendedSettings.enableAlerts) {
      return;
    }

    var lastSGVEntry = sbx.lastSGVEntry();

    if (!lastSGVEntry || lastSGVEntry.mills >= sbx.time) {
      return;
    }

    function buildMessage(agoDisplay) {
      var lines = sbx.prepareDefaultLines();
      lines.unshift(translate('Last received:') + ' ' + [agoDisplay.value, agoDisplay.label].join(' '));
      return lines.join('\n');
    }

    function sendAlarm(opts) {
      var agoDisplay = timeago.calcDisplay(lastSGVEntry, sbx.time);

      sbx.notifications.requestNotify({
        level: opts.level,
        title: translate('Stale data, check rig?'),
        message: buildMessage(agoDisplay),
        eventName: timeago.name,
        plugin: timeago,
        group: 'Time Ago',
        pushoverSound: opts.pushoverSound,
        debug: agoDisplay
      });
    }

    var status = timeago.checkStatus(sbx);
    if (status === 'urgent') {
      sendAlarm({
        level: levels.URGENT,
        pushoverSound: 'echo'
      });
    } else if (status === 'warn') {
      sendAlarm({
        level: levels.WARN,
        pushoverSound: 'echo'
      });
    }

  };

  timeago.checkStatus = function checkStatus(sbx) {

    // Check if the app has been suspended; if yes, snooze data missing alarmn for 15 seconds
    var now = new Date();
    var delta = now.getTime() - lastChecked.getTime();
    lastChecked = now;

    if (delta > 15 * 1000) { // Looks like we've been hibernating
      lastSuspendTime = now;
    }

    var timeSinceLastSuspended = now.getTime() - lastSuspendTime.getTime();

    if (timeSinceLastSuspended < (10 * 1000)) {

      console.log('Hibernation detected, suspending timeago alarm');
      return 'current';
    }

    var lastSGVEntry = sbx.lastSGVEntry(),
      warn = sbx.settings.alarmTimeagoWarn,
      warnMins = sbx.settings.alarmTimeagoWarnMins || 15,
      urgent = sbx.settings.alarmTimeagoUrgent,
      urgentMins = sbx.settings.alarmTimeagoUrgentMins || 30;

    function isStale(mins) {
      return sbx.time - lastSGVEntry.mills > times.mins(mins).msecs;
    }

    var status = 'current';

    if (!lastSGVEntry) {
      //assume current
    } else if (urgent && isStale(urgentMins)) {
      status = 'urgent';
    } else if (warn && isStale(warnMins)) {
      status = 'warn';
    }

    return status;

  };

  timeago.isMissing = function isMissing(opts) {
    if (!opts || !opts.entry || isNaN(opts.entry.mills) || isNaN(opts.time) || isNaN(opts.timeSince)) {
      return {
        label: translate('time ago'),
        shortLabel: translate('ago')
      };
    }
  };

  timeago.inTheFuture = function inTheFuture(opts) {
    if (opts.entry.mills - times.mins(5).msecs > opts.time) {
      return {
        label: translate('in the future'),
        shortLabel: translate('future')
      };
    }
  };

  timeago.almostInTheFuture = function almostInTheFuture(opts) {
    if (opts.entry.mills > opts.time) {
      return {
        value: 1,
        label: translate('min ago'),
        shortLabel: 'm'
      };
    }
  };

  timeago.isLessThan = function isLessThan(limit, divisor, label, shortLabel) {
    return function checkIsLessThan(opts) {
      if (opts.timeSince < limit) {
        return {
          value: Math.max(1, Math.round(opts.timeSince / divisor)),
          label: label,
          shortLabel: shortLabel
        };
      }
    };
  };

  timeago.resolvers = [
    timeago.isMissing, timeago.inTheFuture, timeago.almostInTheFuture, timeago.isLessThan(times.mins(2).msecs, times.min().msecs, 'min ago', 'm'), timeago.isLessThan(times.hour().msecs, times.min().msecs, 'mins ago', 'm'), timeago.isLessThan(times.hours(2).msecs, times.hour().msecs, 'hour ago', 'h'), timeago.isLessThan(times.day().msecs, times.hour().msecs, 'hours ago', 'h'), timeago.isLessThan(times.days(2).msecs, times.day().msecs, 'day ago', 'd'), timeago.isLessThan(times.week().msecs, times.day().msecs, 'days ago', 'd'),
    function () {
      return {
        label: 'long ago',
        shortLabel: 'ago'
      }
    }
  ];

  timeago.calcDisplay = function calcDisplay(entry, time) {
    var opts = {
      time: time,
      entry: entry
    };

    if (time && entry && entry.mills) {
      opts.timeSince = time - entry.mills;
    }

    for (var i = 0; i < timeago.resolvers.length; i++) {
      var value = timeago.resolvers[i](opts);
      if (value) {
        return value;
      }
    }
  };

  timeago.updateVisualisation = function updateVisualisation(sbx) {
    var agoDisplay = timeago.calcDisplay(sbx.lastSGVEntry(), sbx.time);
    var inRetroMode = sbx.data.inRetroMode;

    sbx.pluginBase.updatePillText(timeago, {
      value: inRetroMode ? null : agoDisplay.value,
      label: inRetroMode ? translate('RETRO') : translate(agoDisplay.label)
        //no warning/urgent class when in retro mode
        ,
      pillClass: inRetroMode ? 'current' : timeago.checkStatus(sbx)
    });
  };

  return timeago;

}

module.exports = init;<|endoftext|>


<commit_before>/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.polyfill.VTTCue');

goog.require('shaka.log');
goog.require('shaka.polyfill');

/**
 * @summary A polyfill to provide VTTCue.
 */
shaka.polyfill.VTTCue = class {
  /**
   * Install the polyfill if needed.
   */
  static install() {
    if (window.VTTCue) {
      shaka.log.info('Using native VTTCue.');
      return;
    }

    if (!window.TextTrackCue) {
      shaka.log.error('VTTCue not available.');
      return;
    }

    const constructorLength = TextTrackCue.length;
    if (constructorLength == 3) {
      shaka.log.info('Using VTTCue polyfill from 3 argument TextTrackCue.');
      window['VTTCue'] = shaka.polyfill.VTTCue.from3ArgsTextTrackCue_;
    } else if (constructorLength == 6) {
      shaka.log.info('Using VTTCue polyfill from 6 argument TextTrackCue.');
      window['VTTCue'] = shaka.polyfill.VTTCue.from6ArgsTextTrackCue_;
    } else if (shaka.polyfill.VTTCue.canUse3ArgsTextTrackCue_()) {
      shaka.log.info('Using VTTCue polyfill from 3 argument TextTrackCue.');
      window['VTTCue'] = shaka.polyfill.VTTCue.from3ArgsTextTrackCue_;
    }
  }

  /**
   * Draft spec TextTrackCue with 3 constructor arguments.
   * @see {@link https://bit.ly/2IdyKbA W3C Working Draft 25 October 2012}.
   *
   * @param {number} startTime
   * @param {number} endTime
   * @param {string} text
   * @return {!TextTrackCue}
   * @private
   */
  static from3ArgsTextTrackCue_(startTime, endTime, text) {
    return new window.TextTrackCue(startTime, endTime, text);
  }

  /**
   * Draft spec TextTrackCue with 6 constructor arguments (5th & 6th are
   * optional).
   * @see {@link https://bit.ly/2KaGSP2 W3C Working Draft 29 March 2012}.
   *
   * @param {number} startTime
   * @param {number} endTime
   * @param {string} text
   * @return {!TextTrackCue}
   * @private
   */
  static from6ArgsTextTrackCue_(startTime, endTime, text) {
    const id = startTime + '-' + endTime + '-' + text;
    // Quoting the access to the TextTrackCue object to satisfy the compiler.
    return new window['TextTrackCue'](id, startTime, endTime, text);
  }

  /**
   * IE10, IE11 and Edge return TextTrackCue.length = 0, although they accept 3
   * constructor arguments.
   *
   * @return {boolean}
   * @private
   */
  static canUse3ArgsTextTrackCue_() {
    try {
      return !!shaka.polyfill.VTTCue.from3ArgsTextTrackCue_(1, 2, '');
    } catch (error) {
      return false;
    }
  }
};

shaka.polyfill.register(shaka.polyfill.VTTCue.install);
<commit_msg>fix(text): Fix VTTCue polyfill in uncompiled mode on Edge<commit_after>/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.polyfill.VTTCue');

goog.require('shaka.log');
goog.require('shaka.polyfill');

/**
 * @summary A polyfill to provide VTTCue.
 */
shaka.polyfill.VTTCue = class {
  /**
   * Install the polyfill if needed.
   */
  static install() {
    if (window.VTTCue) {
      shaka.log.info('Using native VTTCue.');
      return;
    }

    if (!window.TextTrackCue) {
      shaka.log.error('VTTCue not available.');
      return;
    }

    /** @type {?function(number, number, string):!TextTrackCue} */
    let replacement = null;
    const constructorLength = TextTrackCue.length;
    if (constructorLength == 3) {
      shaka.log.info('Using VTTCue polyfill from 3 argument TextTrackCue.');
      replacement = shaka.polyfill.VTTCue.from3ArgsTextTrackCue_;
    } else if (constructorLength == 6) {
      shaka.log.info('Using VTTCue polyfill from 6 argument TextTrackCue.');
      replacement = shaka.polyfill.VTTCue.from6ArgsTextTrackCue_;
    } else if (shaka.polyfill.VTTCue.canUse3ArgsTextTrackCue_()) {
      shaka.log.info('Using VTTCue polyfill from 3 argument TextTrackCue.');
      replacement = shaka.polyfill.VTTCue.from3ArgsTextTrackCue_;
    }

    if (!replacement) {
      shaka.log.error('No recognized signature for TextTrackCue found!');
      return;
    }

    // The polyfilled VTTCue must be callable with "new", but the static methods
    // in this class cannot be called that way on legacy Edge.  So we must wrap
    // the replacement in a plain function.
    // eslint-disable-next-line no-restricted-syntax
    window['VTTCue'] = function(start, end, text) {
      return replacement(start, end, text);
    };
  }

  /**
   * Draft spec TextTrackCue with 3 constructor arguments.
   * @see {@link https://bit.ly/2IdyKbA W3C Working Draft 25 October 2012}.
   *
   * @param {number} startTime
   * @param {number} endTime
   * @param {string} text
   * @return {!TextTrackCue}
   * @private
   */
  static from3ArgsTextTrackCue_(startTime, endTime, text) {
    return new window.TextTrackCue(startTime, endTime, text);
  }

  /**
   * Draft spec TextTrackCue with 6 constructor arguments (5th & 6th are
   * optional).
   * @see {@link https://bit.ly/2KaGSP2 W3C Working Draft 29 March 2012}.
   *
   * @param {number} startTime
   * @param {number} endTime
   * @param {string} text
   * @return {!TextTrackCue}
   * @private
   */
  static from6ArgsTextTrackCue_(startTime, endTime, text) {
    const id = startTime + '-' + endTime + '-' + text;
    // Quoting the access to the TextTrackCue object to satisfy the compiler.
    return new window['TextTrackCue'](id, startTime, endTime, text);
  }

  /**
   * IE10, IE11 and Edge return TextTrackCue.length = 0, although they accept 3
   * constructor arguments.
   *
   * @return {boolean}
   * @private
   */
  static canUse3ArgsTextTrackCue_() {
    try {
      return !!shaka.polyfill.VTTCue.from3ArgsTextTrackCue_(1, 2, '');
    } catch (error) {
      return false;
    }
  }
};

shaka.polyfill.register(shaka.polyfill.VTTCue.install);
<|endoftext|>


<commit_before>describe("modal-pattern", function() {
    var pattern;

    requireDependencies(["patterns/modal"], function(cls) {
        pattern = cls;
    });

    // Reset the lab before each test
    beforeEach(function() {
        $("#lab *").remove();
    });

    describe("init", function() {
        it("Modal with single element", function() {
            $("#lab").html([
                '
',
                '
Modal content
',
                '
'
                ].join('\n'));
            var $modal = $("#modal");
            pattern.init($modal);
            expect($modal.find(".header").length).toBeTruthy();
            expect($modal.find(".header").text()).toBe("Close");
            expect($modal.find(".header .close-panel").length).toBeTruthy();
            expect($modal.find(".panel-content").length).toBeTruthy();
            expect($modal.find(".panel-content").text()).toBe("Modal content");
        });

        it("Modal with header ", function() {
            $("#lab").html([
                '
',
                '
Modal header
',
                '
Modal content
',
                '
'
                ].join('\n'));
            var $modal = $("#modal");
            pattern.init($modal);
            expect($modal.find(".header").text()).toBe("Modal headerClose");
            expect($modal.find(".header .close-panel").length).toBeTruthy();
            expect($modal.find(".panel-content").text()).toBe("Modal content");
        });

        it("Modal with multiple content items ", function() {
            $("#lab").html([
                '
',
                '
Modal header
',
                '
Modal content
',
                '
Subheader
',
                '
More content
',
                '
'
                ].join('\n'));
            var $modal = $("#modal");
            pattern.init($modal);
            expect($modal.find(".header").text()).toBe("Modal headerClose");
            expect($modal.find(".header .close-panel").length).toBeTruthy();
            expect($modal.find(".panel-content p").length).toBe(2);
            expect($modal.find(".panel-content h4").length).toBe(1);
        });

    });
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<commit_msg>Add provisional test for #111 fix<commit_after>/*global $, beforeEach, describe, expect, it, requireDependencies*/
describe("modal-pattern", function () {
    "use strict";

    var pattern;

    requireDependencies(["patterns/modal"], function (cls) {
        pattern = cls;
    });

    // Reset the lab before each test
    beforeEach(function () {
        $("#lab *").remove();
    });

    describe("init", function () {
        it("Modal with single element", function () {
            $("#lab").html([
                '
',
                '
Modal content
',
                '
'
            ].join('\n'));
            var $modal = $("#modal");
            pattern.init($modal);
            expect($modal.find(".header").length).toBeTruthy();
            expect($modal.find(".header").text()).toBe("Close");
            expect($modal.find(".header .close-panel").length).toBeTruthy();
            expect($modal.find(".panel-content").length).toBeTruthy();
            expect($modal.find(".panel-content").text()).toBe("Modal content");
        });

        it("Modal with header ", function () {
            $("#lab").html([
                '
',
                '
Modal header
',
                '
Modal content
',
                '
'
            ].join('\n'));
            var $modal = $("#modal");
            pattern.init($modal);
            expect($modal.find(".header").text()).toBe("Modal headerClose");
            expect($modal.find(".header .close-panel").length).toBeTruthy();
            expect($modal.find(".panel-content").text()).toBe("Modal content");
        });

        it("Modal with multiple content items ", function () {
            $("#lab").html([
                '
',
                '
Modal header
',
                '
Modal content
',
                '
Subheader
',
                '
More content
',
                '
'
            ].join('\n'));
            var $modal = $("#modal");
            pattern.init($modal);
            expect($modal.find(".header").text()).toBe("Modal headerClose");
            expect($modal.find(".header .close-panel").length).toBeTruthy();
            expect($modal.find(".panel-content p").length).toBe(2);
            expect($modal.find(".panel-content h4").length).toBe(1);
        });

        it("Modal with a form that has the pat-modal CSS class", function () {

            var $modal,       // main modal container
                $modalLink;   // link that triggers the modal

            $("#lab").html([
                'Open a modal',
                '   containing a form with pat-modal CSS class.'
            ].join('/n'));

            $modalLink = $("#modalLink");
            pattern.init($modalLink);

            // najprej #modalLink ne obstaja
            $modal = $("div#pat-modal");
            expect($modal.length).toBe(0);

            $modalLink.click();    // trigger panel loading

            // TODO: we need to insert a delay here so that the asynchronous
            // AJAX injection of the potentially problematic HTML finishes ...
            // and only AFTER THAT we test that everything is still OK
            // (read: that the modal still exists in DOM)
            // NOTE: If AJAX request in inject.execute() is performed in a
            // synchronous way, a delay is not need and this test detects a
            // bug in modal._init_inject1()

            // div#pat-modal should still exist even after the click has
            // loaded and injected problematic HTML
            $modal = $("div#pat-modal");
            expect($modal.length).toBeGreaterThan(0);
        });
    });
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<|endoftext|>


<commit_before>export default DS.RESTAdapter.extend({

  //db: new PouchDB('http://localhost:5984/ember-todo'),

  _init: function (type) {
    var self = this;
    if (!this.db || typeof this.db !== 'object') {
      throw new Error('Please set the `db` property on the adapter.');
    }

    this._schema = this._schema || [];

    var singular = type.typeKey;
    var plural = Ember.String.pluralize(type.typeKey);

    // check that we haven't already registered this model
    for (var i = 0, len = this._schema.length; i < len; i++) {
      var currentSchemaDef = this._schema[i];
      if (currentSchemaDef.singular === singular) {
        return;
      }
    }

    var schemaDef = {
      singular: singular,
      plural: plural
    };

    // else it's new, so update
    this._schema.push(schemaDef);

    // check all the subtypes
    type.eachRelationship(function (_, rel) {
      if (rel.kind !== 'belongsTo' && rel.kind !== 'hasMany') {
        // TODO: support inverse as well
        return; // skip
      }
      var relDef = {};
      relDef[rel.kind] = rel.type.typeKey;
      if (!schemaDef.relations) {
        schemaDef.relations = {};
      }
      schemaDef.relations[rel.key] = relDef;
      self._init(rel.type);
    });

    this.db.setSchema(this._schema);
  },

  _recordToData: function (store, type, record) {
    var data = {};
    var serializer = store.serializerFor(type.typeKey);

    serializer.serializeIntoHash(data, type, record, { includeId: true });

    data = data[type.typeKey];

    // ember sets it to null automatically. don't need it.
    if (data.rev === null) {
      delete data.rev;
    }

    return data;
  },

  findAll: function(store, type /*, sinceToken */) {
    // TODO: use sinceToken
    this._init(type);
    return this.db.rel.find(type.typeKey);
  },

  findMany: function(store, type, ids) {
    this._init(type);
    return this.db.rel.find(type.typeKey, ids);
  },

  find: function (store, type, id) {
    this._init(type);
    return this.db.rel.find(type.typeKey, id).then(function (payload) {
      // Ember Data chokes on empty payload, this function throws
      // an error when the requested data is not found
      if (typeof payload === 'object' && payload !== null) {
        var singular = type.typeKey;
        var plural = Ember.String.pluralize(type.typeKey);
        var results = payload[singular] || payload[plural];
        if (results && results.length > 0) {
          return payload;
        }
      }
      throw new Error('Not found: type "' + type.typeKey +
        '" with id "' + id + '"');
    });
  },

  createRecord: function(store, type, record) {
    this._init(type);
    var data = this._recordToData(store, type, record);
    return this.db.rel.save(type.typeKey, data);
  },

  updateRecord: function (store, type, record) {
    this._init(type);
    var data = this._recordToData(store, type, record);
    return this.db.rel.save(type.typeKey, data);
  },

  deleteRecord: function (store, type, record) {
    this._init(type);
    var data = this._recordToData(store, type, record);
    return this.db.rel.del(type.typeKey, data).then(function () {
      return ''; // ember doesn't like getting a json response of {deleted: true}
    });
  }
});
<commit_msg>Pass along relationship options<commit_after>export default DS.RESTAdapter.extend({

  //db: new PouchDB('http://localhost:5984/ember-todo'),

  _init: function (type) {
    var self = this;
    if (!this.db || typeof this.db !== 'object') {
      throw new Error('Please set the `db` property on the adapter.');
    }

    this._schema = this._schema || [];

    var singular = type.typeKey;
    var plural = Ember.String.pluralize(type.typeKey);

    // check that we haven't already registered this model
    for (var i = 0, len = this._schema.length; i < len; i++) {
      var currentSchemaDef = this._schema[i];
      if (currentSchemaDef.singular === singular) {
        return;
      }
    }

    var schemaDef = {
      singular: singular,
      plural: plural
    };

    // else it's new, so update
    this._schema.push(schemaDef);

    // check all the subtypes
    type.eachRelationship(function (_, rel) {
      if (rel.kind !== 'belongsTo' && rel.kind !== 'hasMany') {
        // TODO: support inverse as well
        return; // skip
      }
      var relDef = {};
      relDef[rel.kind] = {
        type: rel.type.typeKey,
        options: rel.options
      };
      if (!schemaDef.relations) {
        schemaDef.relations = {};
      }
      schemaDef.relations[rel.key] = relDef;
      self._init(rel.type);
    });

    this.db.setSchema(this._schema);
  },

  _recordToData: function (store, type, record) {
    var data = {};
    var serializer = store.serializerFor(type.typeKey);

    serializer.serializeIntoHash(data, type, record, { includeId: true });

    data = data[type.typeKey];

    // ember sets it to null automatically. don't need it.
    if (data.rev === null) {
      delete data.rev;
    }

    return data;
  },

  findAll: function(store, type /*, sinceToken */) {
    // TODO: use sinceToken
    this._init(type);
    return this.db.rel.find(type.typeKey);
  },

  findMany: function(store, type, ids) {
    this._init(type);
    return this.db.rel.find(type.typeKey, ids);
  },

  find: function (store, type, id) {
    this._init(type);
    return this.db.rel.find(type.typeKey, id).then(function (payload) {
      // Ember Data chokes on empty payload, this function throws
      // an error when the requested data is not found
      if (typeof payload === 'object' && payload !== null) {
        var singular = type.typeKey;
        var plural = Ember.String.pluralize(type.typeKey);
        var results = payload[singular] || payload[plural];
        if (results && results.length > 0) {
          return payload;
        }
      }
      throw new Error('Not found: type "' + type.typeKey +
        '" with id "' + id + '"');
    });
  },

  createRecord: function(store, type, record) {
    this._init(type);
    var data = this._recordToData(store, type, record);
    return this.db.rel.save(type.typeKey, data);
  },

  updateRecord: function (store, type, record) {
    this._init(type);
    var data = this._recordToData(store, type, record);
    return this.db.rel.save(type.typeKey, data);
  },

  deleteRecord: function (store, type, record) {
    this._init(type);
    var data = this._recordToData(store, type, record);
    return this.db.rel.del(type.typeKey, data).then(function () {
      return ''; // ember doesn't like getting a json response of {deleted: true}
    });
  }
});
<|endoftext|>


<commit_before>function isRoute (obj) {
  return Route.isPrototypeOf(obj)
}

Tinytest.add('Route.action', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.action, 'function', 'Route should have a function action')
  test.isTrue(isRoute(route.action(function () {})), 'It should return the route that it\'s beeing called on.')
  test.equal(typeof route._actionValue, 'function', 'It should set the passed function to Route._actionValue')
  Route.action(function () {})
  test.equal(typeof route._actionGlobalValue, 'function', 'It should set the action to _actionGlobalValue if it\'s called on the global object')
})

Tinytest.add('Route.enter', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.enter, 'function', 'Route should have a function enter')
  test.isTrue(isRoute(route.enter(function () {})), 'It should return the route that it\'s beeing called on.')
  test.equal(route._enterValue.length, 1, 'It should push the passed functions to Route._enterValue')
  Route.enter(function () {})
  test.equal(route._enterGlobalValue.length, 1, 'It should add the enter hooks to _enterGlobalValue if it\'s called on the global object')
})

Tinytest.add('Route.exit', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.exit, 'function', 'Route should have a function exit')
  test.isTrue(isRoute(route.exit(function () {})), 'It should return the route that it\'s beeing called on.')
  test.equal(route._exitValue.length, 1, 'It should push the passed functions to Route._exitValue')
  Route.exit(function () {})
  test.equal(route._exitGlobalValue.length, 1, 'It should add the exit hooks to _exitGlobalValue if it\'s called on the global object')
})

Tinytest.add('Route.set', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.set, 'function', 'Route should have a function set')
  test.isTrue(isRoute(route.set('key', 'value')), 'It should return the route that it\'s beeing called on.')
  test.equal(route['key'], 'value', 'It should set the value of the passed key to the passed value')
  test.equal(route.set('key', function () {return 'else'}).key, 'else', 'If a function is passed as a value the function should be called and the return value should be set as the value')
})

Tinytest.add('Route.call', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.call, 'function', 'Route should have a function call')
  route._testValue = function () {return true}
  test.isTrue(route.call('test'), 'It should call the function with the function under the key prefixed by an underscore and suffixed by "Value".')
  test.isTrue(isRoute(route.call('test')), 'It should return the route that it\'s beeing caled on')
  route._testGlobGlobalValue = function () {return true}
  test.isTrue(isRoute(route.call('testGlob')), 'It should call the function under the key suffixed with GlobalValue if the one with Value doesn\'t exist')
  var shouldEqualTest = false
  route._test42Value = function (str) {
    shouldEqualTest = str
  }
  test.equal((route.call('test42', 'test') && shouldEqualTest), 'test', 'It should pass parameters after the first one to the called function')
})

Tinytest.add('Route.callAll', function (test) {
  var route = Object.create(Route)
  test.equal(typeof Route.callAll, 'function', 'Route should hava a function called callAll')
  test.isTrue(isRoute(route.callAll()), 'It should return a route')
  var shouldBeTrue = false
  var shouldBeFalse = true
  route._testValue = [
    function (next) {
      shouldBeTrue = false
      next()
    },
    function (next) {
      shouldBeTrue = true
      next()
    }
  ]
  test.isTrue((route.callAll('test') && shouldBeTrue), 'It should call all functions inside _[key]Value')
  Route._testGlobalValue = [
    function (next) {
      shouldBeFalse = true
      next()
    },
    function (next) {
      shouldBeFalse = false
      next()
    }
  ]
  test.isTrue((route.callAll('test') && !shouldBeFalse), 'It should also call all functions inside _[key]GlobalValue')
  shouldAlsoBeTrue = false
  route._test2Value = [function (val) {
    shouldAlsoBeTrue = val
  }]
  test.isTrue((route.callAll('test2', true) && shouldAlsoBeTrue), 'It should pass an array of parameters to the called functions.')
})

Tinytest.add('Route.match', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.match, 'function', 'Route should have a function match.')
  route.pathRegex = /\w+/g
  test.equal(typeof route.match('asdf'), 'object', 'It should return an array when the regex matches matches')
  test.isFalse(route.match('asdf12*'), 'It should return false if the regex doesn\'t match')
})
<commit_msg>Fix the tests for callAll<commit_after>function isRoute (obj) {
  return Route.isPrototypeOf(obj)
}

Tinytest.add('Route.action', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.action, 'function', 'Route should have a function action')
  test.isTrue(isRoute(route.action(function () {})), 'It should return the route that it\'s beeing called on.')
  test.equal(typeof route._actionValue, 'function', 'It should set the passed function to Route._actionValue')
  Route.action(function () {})
  test.equal(typeof route._actionGlobalValue, 'function', 'It should set the action to _actionGlobalValue if it\'s called on the global object')
})

Tinytest.add('Route.enter', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.enter, 'function', 'Route should have a function enter')
  test.isTrue(isRoute(route.enter(function () {})), 'It should return the route that it\'s beeing called on.')
  test.equal(route._enterValue.length, 1, 'It should push the passed functions to Route._enterValue')
  Route.enter(function () {})
  test.equal(route._enterGlobalValue.length, 1, 'It should add the enter hooks to _enterGlobalValue if it\'s called on the global object')
})

Tinytest.add('Route.exit', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.exit, 'function', 'Route should have a function exit')
  test.isTrue(isRoute(route.exit(function () {})), 'It should return the route that it\'s beeing called on.')
  test.equal(route._exitValue.length, 1, 'It should push the passed functions to Route._exitValue')
  Route.exit(function () {})
  test.equal(route._exitGlobalValue.length, 1, 'It should add the exit hooks to _exitGlobalValue if it\'s called on the global object')
})

Tinytest.add('Route.set', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.set, 'function', 'Route should have a function set')
  test.isTrue(isRoute(route.set('key', 'value')), 'It should return the route that it\'s beeing called on.')
  test.equal(route['key'], 'value', 'It should set the value of the passed key to the passed value')
  test.equal(route.set('key', function () {return 'else'}).key, 'else', 'If a function is passed as a value the function should be called and the return value should be set as the value')
})

Tinytest.add('Route.call', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.call, 'function', 'Route should have a function call')
  route._testValue = function () {return true}
  test.isTrue(route.call('test'), 'It should call the function with the function under the key prefixed by an underscore and suffixed by "Value".')
  test.isTrue(isRoute(route.call('test')), 'It should return the route that it\'s beeing caled on')
  route._testGlobGlobalValue = function () {return true}
  test.isTrue(isRoute(route.call('testGlob')), 'It should call the function under the key suffixed with GlobalValue if the one with Value doesn\'t exist')
  var shouldEqualTest = false
  route._test42Value = function (str) {
    shouldEqualTest = str
  }
  test.equal((route.call('test42', 'test') && shouldEqualTest), 'test', 'It should pass parameters after the first one to the called function')
})

Tinytest.add('Route.callAll', function (test) {
  var route = Object.create(Route)
  test.equal(typeof Route.callAll, 'function', 'Route should hava a function called callAll')
  test.isTrue(isRoute(route.callAll()), 'It should return a route')
  var shouldBe3 = 0
  var shouldBe6 = true
  route._testValue = [
    function (next) {
      shouldBeTrue += 1
      next()
    },
    function (next) {
      shouldBeTrue += 2
      next()
    }
  ]
  test.isEqual(shouldBe3, 3, 'It should call all functions inside _[key]Value')
  Route._testGlobalValue = [
    function (next) {
      shouldBeFalse += 1
      next()
    },
    function (next) {
      shouldBeFalse += 2
      next()
    }
  ]
  test.isTrue(shouldBe6, 6, 'It should also call all functions inside _[key]GlobalValue')
  shouldAlsoBeTrue = false
  route._test2Value = [function (val) {
    shouldAlsoBeTrue = val
  }]
  test.isTrue((route.callAll('test2', true) && shouldAlsoBeTrue), 'It should pass an array of parameters to the called functions.')
})

Tinytest.add('Route.match', function (test) {
  var route = Object.create(Route)
  test.equal(typeof route.match, 'function', 'Route should have a function match.')
  route.pathRegex = /\w+/g
  test.equal(typeof route.match('asdf'), 'object', 'It should return an array when the regex matches matches')
  test.isFalse(route.match('asdf12*'), 'It should return false if the regex doesn\'t match')
})
<|endoftext|>


<commit_before>var redis = require('mongodb');

module.exports = (function() {

    var PulsarMongoDB = function(host, port) {
        this.host = host;
        this.port = port;
    }

    PulsarRedisDB.DATABASE_NAME = 'pulsar';
    PulsarRedisDB.COLLECTION_NAME = 'tasks';

    PulsarMongoDB.prototype.getUniqueTaskID = function() {
        return Math.round(100 * Math.random());
    }

    PulsarMongoDB.prototype.connect = function() {
        var self = this;
        var url = 'mongodb://' + this.host + ':' + this.port + '/' + PulsarRedisDB.DATABASE_NAME;

        this.db = MongoClient.connect(url, function(err, db) {
            if(err) throw err;
            self.collection = self.db.collection(PulsarRedisDB.COLLECTION_NAME);
        });
    }

    PulsarMongoDB.prototype.insert = function(id, data) {
    }

    PulsarMongoDB.prototype.select = function(id, callback) {
    }

    PulsarMongoDB.prototype.delete = function() {
    }

    PulsarMongoDB.prototype.update = function(id, data) {
    }

    return PulsarMongoDB;

})();<commit_msg>Fixed class name<commit_after>var redis = require('mongodb');

module.exports = (function() {

    var PulsarMongoDB = function(host, port) {
        this.host = host;
        this.port = port;
    }

    PulsarMongoDB.DATABASE_NAME = 'pulsar';
    PulsarMongoDB.COLLECTION_NAME = 'tasks';

    PulsarMongoDB.prototype.getUniqueTaskID = function() {
        return Math.round(100 * Math.random());
    }

    PulsarMongoDB.prototype.connect = function() {
        var self = this;
        var url = 'mongodb://' + this.host + ':' + this.port + '/' + PulsarRedisDB.DATABASE_NAME;

        this.db = MongoClient.connect(url, function(err, db) {
            if(err) throw err;
            self.collection = self.db.collection(PulsarRedisDB.COLLECTION_NAME);
        });
    }

    PulsarMongoDB.prototype.insert = function(id, data) {
    }

    PulsarMongoDB.prototype.select = function(id, callback) {
    }

    PulsarMongoDB.prototype.delete = function() {
    }

    PulsarMongoDB.prototype.update = function(id, data) {
    }

    return PulsarMongoDB;

})();<|endoftext|>


<commit_before>var Base = require('./base');
var sys  = require('sys');

/**
 * Expose `XUnit`.
 */

exports = module.exports = XUnit;

function XUnit(runner) {
  Base.call(this, runner);
  var stats = this.stats;
  var tests = [];
  var self = this;

  runner.on('test end', function(test) {
    tests.push(test);
  });

  runner.on('end', function() {
    console.error(tag('testsuite', {
        name: 'Mocha Tests',
        tests: stats.tests,
        failures: stats.failures,
        skip: (stats.tests - stats.failures - stats.passes),
        timestamp: (new Date).toUTCString(),
        time: stats.duration / 1000.0
    }, false));

    var _i, _len;
    for (_i = 0; _len = tests.length, _i < _len; _i++) {
      processTest(tests[_i]);
    }

    console.error(end('testsuite'));
  });
}

function processTest(test) {
  var attributes = {
    classname: test.fullTitle(),
    name: test.title,
    time: test.duration / 1000.0
  };

  if (test.failed) {
    console.error( tag('testcase', attributes, false, failedTestTag(test)) );
  } else if (test.pending) {
    console.error( tag('testcase', attributes, false, pendingTestTag(test)) );
  } else {
    console.error( tag('testcase', attributes, true) );
  }
}

function pendingTestTag(test) {
  return tag('skipped', { message: encode(test.err.stack || test.err) }, true);
}

function failedTestTag(test) {
  var str = test.err;

  // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
  // check for the result of the stringifying.
  if ('[object Error]' == str) str = test.err.message;

  var attributes = { message: encode(str) };
  return tag('failure', attributes, false, cdata(test.err.stack));
}

function encode(value) {
  return !value ? value :
    String(value)
      .replace(/&/g, '&')
      .replace(/>/g, '>')
      .replace(/</g, '<')
      .replace(/"/g, '"')
      .replace(/\u001b\[\d{1,2}m/g, '');
}

function tag(name, attribs, single, content) {
  var tag;
  var end = single ? ' />' : '>'
  var strAttr = [];
  for (var attr in attribs) {
    attribs.hasOwnProperty(attr) && strAttr.push(attr + '="' + encode(attribs[attr]) + '"');
  }

  tag = '<' + name + (strAttr.length? ' ' + strAttr.join(' ') : '' ) + end;
  tag = content? (tag + content + '</' + name + end) : tag;
  return tag;
}

function end(name) {
  return '</' + name + '>';
}

function cdata(data) {
  return '<![CDATA[' + encode(data) + ']]>';
}

XUnit.prototype.__proto__ = Base.prototype;
<commit_msg>Removing sys require, and using utils.escape<commit_after>var Base = require('./base');
var utils = require('../utils');

/**
 * Expose `XUnit`.
 */

exports = module.exports = XUnit;

function XUnit(runner) {
  Base.call(this, runner);
  var stats = this.stats;
  var tests = [];
  var self = this;

  runner.on('test end', function(test) {
    tests.push(test);
  });

  runner.on('end', function() {
    console.error(tag('testsuite', {
        name: 'Mocha Tests',
        tests: stats.tests,
        failures: stats.failures,
        skip: (stats.tests - stats.failures - stats.passes),
        timestamp: (new Date).toUTCString(),
        time: stats.duration / 1000.0
    }, false));

    var _i, _len;
    for (_i = 0; _len = tests.length, _i < _len; _i++) {
      processTest(tests[_i]);
    }

    console.error(end('testsuite'));
  });
}

function processTest(test) {
  var attributes = {
    classname: test.fullTitle(),
    name: test.title,
    time: test.duration / 1000.0
  };

  if (test.failed) {
    console.error( tag('testcase', attributes, false, failedTestTag(test)) );
  } else if (test.pending) {
    console.error( tag('testcase', attributes, false, pendingTestTag(test)) );
  } else {
    console.error( tag('testcase', attributes, true) );
  }
}

function pendingTestTag(test) {
  return tag('skipped', { message: utils.escape(test.err.stack || test.err) }, true);
}

function failedTestTag(test) {
  var str = test.err;

  // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
  // check for the result of the stringifying.
  if ('[object Error]' == str) str = test.err.message;

  var attributes = { message: utils.escape(str) };
  return tag('failure', attributes, false, cdata(test.err.stack));
}

function tag(name, attribs, single, content) {
  var tag;
  var end = single ? ' />' : '>'
  var strAttr = [];
  for (var attr in attribs) {
    attribs.hasOwnProperty(attr) && strAttr.push(attr + '="' + utils.escape(attribs[attr]) + '"');
  }

  tag = '<' + name + (strAttr.length? ' ' + strAttr.join(' ') : '' ) + end;
  tag = content? (tag + content + '</' + name + end) : tag;
  return tag;
}

function end(name) {
  return '</' + name + '>';
}

function cdata(data) {
  return '<![CDATA[' + utils.escape(data) + ']]>';
}

XUnit.prototype.__proto__ = Base.prototype;
<|endoftext|>