<commit_before>'use strict';

function FloatingCursor(world) {
    this.__init__(world);
}

FloatingCursor.prototype = {

    __init__: function(world) {
        this.world                 = world;
        this._cursors              = {};
        this.currently_attached_to = null;

        this._current_cursor       = null;
        this._previous_cursor      = null;

        this._currently_engaged    = false;
    },

    wheel_event: function(delta) {
        switch (delta) {
        case 1:
            this._horizontal_distance_to_player *= 1.1;
            break;
        case -1:
            this._horizontal_distance_to_player *= 0.9;
            break;
        }
    },

    engage: function() {
        this._currently_engaged = true;

        var h = this.currently_attached_to.height;

        var cursor_position = this.cursor_wall.get_position();

        this._previous_vertical_percentage = ((this.currently_attached_to.object3D.position.y + h / 2) - cursor_position.y ) / h;

        // Save the initial position at the engage.
        this._previous_y = cursor_position.y;

        var pp = CURRENT_PLAYER.get_position();

        this._horizontal_distance_to_player = this.currently_attached_to.get_horizontal_distance_to_center(pp.x, pp.z);

        // TODO : TEMPORARY MEASURE.
        this._current_cursor.set_to_invisible();
    },

    disengage: function() {
        this._currently_engaged = false;

        // TODO : TEMPORARY MEASURE.
        this._current_cursor.set_to_visible();

        var plane_current_position = this.currently_attached_to.get_position();
        var player_parametric_equation = get_parametric_line_equation(CURRENT_PLAYER.get_position(), CURRENT_PLAYER.get_direction());
        var plane_parametric_equation = get_parametric_plane_equation(plane_current_position, this.currently_attached_to.get_normal());
        var current_position = get_line_intersection_on_infinite_plane(player_parametric_equation, plane_parametric_equation);

        if (this._is_current_cursor_type(CURSOR_TYPE_VERTICAL)) {
            var h = this.currently_attached_to.height;

            var current_vertical_percentage = ((this.currently_attached_to.object3D.position.y + h / 2) - current_position[1] ) / h;

            // TODO : FIX HEIGHT CHANGES FROM THE BOTTOM!!!!

            current_vertical_percentage *= 2;
            current_vertical_percentage *= -1;
            this.currently_attached_to.update_height(1 + current_vertical_percentage);
            this.currently_attached_to.refresh_position_and_look_at();

        } else if (this._is_current_cursor_type(CURSOR_TYPE_HORIZONTAL)) {

            var w = this.currently_attached_to.width;

            var current_horizontal_percentage = this.currently_attached_to.get_horizontal_distance_to_center(current_position[0], current_position[2]) / w;

            current_horizontal_percentage = 0.5 - current_horizontal_percentage;
            current_horizontal_percentage *= 2;
            current_horizontal_percentage *= -1;

            //current_vertical_percentage *= -1;
            this.currently_attached_to.update_width(1 + current_horizontal_percentage);
            this.currently_attached_to.refresh_position_and_look_at();
        }

    },

    attach: function(object_to_attach_to) {
        this.currently_attached_to = object_to_attach_to;

        if (this.currently_attached_to.type === TYPE_BUTTON || this.currently_attached_to.type === TYPE_CHECK_BOX) {
            this._set_current_cursor(CURSOR_TYPE_HAND);
        } else if (this.currently_attached_to.scalable) {
            this._get_scalable_cursor_needed();
        } else {
            this._set_current_cursor(CURSOR_TYPE_POINTER);
        }
    },

    detach: function() {
        this._current_cursor.set_to_invisible();

        this.currently_attached_to = null;
        this._previous_cursor      = null;
        this._current_cursor       = null;
    },

    update_position: function(p) {
        var normal = this.currently_attached_to.get_normal();

        this.cursor_wall.set_position(p.x, p.y, p.z, false);
        this.cursor_wall.set_normal(normal.x, normal.y, normal.z, false);
        this.cursor_wall.refresh_position_and_look_at();
    },

    update: function() {
        if (this._currently_engaged) {

            var plane_current_position = this.currently_attached_to.get_position();

            var player_parametric_equation = get_parametric_line_equation(CURRENT_PLAYER.get_position(), CURRENT_PLAYER.get_direction());
            var plane_parametric_equation = get_parametric_plane_equation(plane_current_position, this.currently_attached_to.get_normal());

            var current_position = get_line_intersection_on_infinite_plane(player_parametric_equation, plane_parametric_equation);

            if (this._is_current_cursor_type(CURSOR_TYPE_MOUSE)) {
                var pp = CURRENT_PLAYER.get_position();
                var pn = CURRENT_PLAYER.get_direction();

                var new_x_position = pp.x + pn.x * this._horizontal_distance_to_player;
                var new_z_position = pp.z + pn.z * this._horizontal_distance_to_player;

                this.currently_attached_to.set_position(new_x_position, (current_position[1] - this._previous_y) + plane_current_position.y, new_z_position);
                this._previous_y = current_position[1];

                this.currently_attached_to.set_normal(-pn.x, 0, -pn.z);
                this.currently_attached_to.refresh_position_and_look_at();
            } else if (this._is_current_cursor_type(CURSOR_TYPE_CSS)) {
                l('PERFORM CSS ACTION!!');
            }

        } else {
            if (is_defined(this.currently_attached_to)) {
                if (this.currently_attached_to.scalable) {
                    this._get_scalable_cursor_needed();
                }
            }
        }
    },

    _get_scalable_cursor_needed: function() {
        var h = this.currently_attached_to.height;
        var w = this.currently_attached_to.width;

        var cursor_position = this.cursor_wall.get_position();

        var vertical_percentage = ((this.currently_attached_to.object3D.position.y + h / 2) - cursor_position.y ) / h;
        var horizontal_percentage = this.currently_attached_to.get_horizontal_distance_to_center(cursor_position.x, cursor_position.z) / w;

        var scroll_vertical   = false;
        var scroll_horizontal = false;

        if (vertical_percentage < 0.03 || vertical_percentage > 0.97) {
            scroll_vertical = true;
        }

        if (horizontal_percentage > .47) {
            scroll_horizontal = true;
        }

        var cursor_already_set = false;
        if (is_defined(this.currently_attached_to.block_input_on_inner_70_percent)) {
            if (this.currently_attached_to.block_input_on_inner_70_percent) {
                l('CHECKING!');
                l(vertical_percentage);
                l(horizontal_percentage);
                if (vertical_percentage > .3 && vertical_percentage < .7 && horizontal_percentage < .35) {
                    this._set_current_cursor(CURSOR_TYPE_CSS);
                }
            }
        }

        if (!cursor_already_set) {
            if (scroll_horizontal && scroll_vertical) {
                this._set_current_cursor(CURSOR_TYPE_LARGER);
            } else if (scroll_vertical) {
                this._set_current_cursor(CURSOR_TYPE_VERTICAL);
            } else if (scroll_horizontal) {
                this._set_current_cursor(CURSOR_TYPE_HORIZONTAL);
            } else {
                this._set_current_cursor(CURSOR_TYPE_MOUSE);
            }
        }
    },

    _set_current_cursor: function(cursor) {
        if (this._current_cursor !== this._cursors[cursor]) {
            this._previous_cursor = this._current_cursor;
            if (is_defined(this._previous_cursor)) {
                this._previous_cursor.set_to_invisible();
            }
            this._current_cursor = this._cursors[cursor];
            this._current_cursor.set_to_visible();
        }
    },

    _is_current_cursor_type: function(cursor_type) {
        return this._current_cursor.get_text() === cursor_type;
    },

    /*        ___                      __        __
     | |\ | |  |  |  /\  |       |    /  \  /\  |  \
     | | \| |  |  | /~~\ |___    |___ \__/ /~~\ |__/ */
    create: function() {
        this.cursor_wall = new FloatingWall(16, 16, null, null, this.world, false);
        this.cursor_wall.set_to_invisible();
        var cursor_row = this.cursor_wall.add_row(0);
        this._create_cursor(CURSOR_TYPE_HORIZONTAL, cursor_row);
        this._create_cursor(CURSOR_TYPE_VERTICAL, cursor_row);
        this._create_cursor(CURSOR_TYPE_HAND, cursor_row);
        this._create_cursor(CURSOR_TYPE_POINTER, cursor_row);
        this._create_cursor(CURSOR_TYPE_LARGER, cursor_row);
        this._create_cursor(CURSOR_TYPE_MOUSE, cursor_row);
        this._create_cursor(CURSOR_TYPE_CSS, cursor_row);
    },

    _create_cursor: function(cursor_type, cursor_row) {
        var c = cursor_row.add_2D_element([0, 1], cursor_type, TYPE_ICON);
        c.set_attachment_depth_offset(6);
        c.set_attachment_horizontal_offset(8, null);
        c.set_attachment_vertical_offset(-8, null);
        c.set_to_invisible();

        // TODO : Transparency issues still exist.
        // Needed for fixing transparency issues. This has the cursor be rendered last.
        c.mesh.renderDepth = -1;

        this._cursors[cursor_type] = c;
    }

};<commit_msg>general updating 84<commit_after>'use strict';

function FloatingCursor(world) {
    this.__init__(world);
}

FloatingCursor.prototype = {

    __init__: function(world) {
        this.world                 = world;
        this._cursors              = {};
        this.currently_attached_to = null;

        this._current_cursor       = null;
        this._previous_cursor      = null;

        this._currently_engaged    = false;
    },

    wheel_event: function(delta) {
        switch (delta) {
        case 1:
            this._horizontal_distance_to_player *= 1.1;
            break;
        case -1:
            this._horizontal_distance_to_player *= 0.9;
            break;
        }
    },

    engage: function() {
        this._currently_engaged = true;

        var h = this.currently_attached_to.height;

        var cursor_position = this.cursor_wall.get_position();

        this._previous_vertical_percentage = ((this.currently_attached_to.object3D.position.y + h / 2) - cursor_position.y ) / h;

        // Save the initial position at the engage.
        this._previous_y = cursor_position.y;

        var pp = CURRENT_PLAYER.get_position();

        this._horizontal_distance_to_player = this.currently_attached_to.get_horizontal_distance_to_center(pp.x, pp.z);

        // TODO : TEMPORARY MEASURE.
        this._current_cursor.set_to_invisible();
    },

    disengage: function() {
        this._currently_engaged = false;

        // TODO : TEMPORARY MEASURE.
        this._current_cursor.set_to_visible();

        var plane_current_position = this.currently_attached_to.get_position();
        var player_parametric_equation = get_parametric_line_equation(CURRENT_PLAYER.get_position(), CURRENT_PLAYER.get_direction());
        var plane_parametric_equation = get_parametric_plane_equation(plane_current_position, this.currently_attached_to.get_normal());
        var current_position = get_line_intersection_on_infinite_plane(player_parametric_equation, plane_parametric_equation);

        if (this._is_current_cursor_type(CURSOR_TYPE_VERTICAL)) {
            var h = this.currently_attached_to.height;

            var current_vertical_percentage = ((this.currently_attached_to.object3D.position.y + h / 2) - current_position[1] ) / h;

            // TODO : FIX HEIGHT CHANGES FROM THE BOTTOM!!!!

            current_vertical_percentage *= 2;
            current_vertical_percentage *= -1;
            this.currently_attached_to.update_height(1 + current_vertical_percentage);
            this.currently_attached_to.refresh_position_and_look_at();

        } else if (this._is_current_cursor_type(CURSOR_TYPE_HORIZONTAL)) {

            var w = this.currently_attached_to.width;

            var current_horizontal_percentage = this.currently_attached_to.get_horizontal_distance_to_center(current_position[0], current_position[2]) / w;

            current_horizontal_percentage = 0.5 - current_horizontal_percentage;
            current_horizontal_percentage *= 2;
            current_horizontal_percentage *= -1;

            //current_vertical_percentage *= -1;
            this.currently_attached_to.update_width(1 + current_horizontal_percentage);
            this.currently_attached_to.refresh_position_and_look_at();
        }

    },

    attach: function(object_to_attach_to) {
        this.currently_attached_to = object_to_attach_to;

        if (this.currently_attached_to.type === TYPE_BUTTON || this.currently_attached_to.type === TYPE_CHECK_BOX) {
            this._set_current_cursor(CURSOR_TYPE_HAND);
        } else if (this.currently_attached_to.scalable) {
            this._get_scalable_cursor_needed();
        } else {
            this._set_current_cursor(CURSOR_TYPE_POINTER);
        }
    },

    detach: function() {
        this._current_cursor.set_to_invisible();

        this.currently_attached_to = null;
        this._previous_cursor      = null;
        this._current_cursor       = null;
    },

    update_position: function(p) {
        var normal = this.currently_attached_to.get_normal();

        this.cursor_wall.set_position(p.x, p.y, p.z, false);
        this.cursor_wall.set_normal(normal.x, normal.y, normal.z, false);
        this.cursor_wall.refresh_position_and_look_at();
    },

    update: function() {
        if (this._currently_engaged) {

            var plane_current_position = this.currently_attached_to.get_position();

            var player_parametric_equation = get_parametric_line_equation(CURRENT_PLAYER.get_position(), CURRENT_PLAYER.get_direction());
            var plane_parametric_equation = get_parametric_plane_equation(plane_current_position, this.currently_attached_to.get_normal());

            var current_position = get_line_intersection_on_infinite_plane(player_parametric_equation, plane_parametric_equation);

            if (this._is_current_cursor_type(CURSOR_TYPE_MOUSE)) {
                var pp = CURRENT_PLAYER.get_position();
                var pn = CURRENT_PLAYER.get_direction();

                var new_x_position = pp.x + pn.x * this._horizontal_distance_to_player;
                var new_z_position = pp.z + pn.z * this._horizontal_distance_to_player;

                this.currently_attached_to.set_position(new_x_position, (current_position[1] - this._previous_y) + plane_current_position.y, new_z_position);
                this._previous_y = current_position[1];

                this.currently_attached_to.set_normal(-pn.x, 0, -pn.z);
                this.currently_attached_to.refresh_position_and_look_at();
            } else if (this._is_current_cursor_type(CURSOR_TYPE_CSS)) {
                l('PERFORM CSS ACTION!!');
            }

        } else {
            if (is_defined(this.currently_attached_to)) {
                if (this.currently_attached_to.scalable) {
                    this._get_scalable_cursor_needed();
                }
            }
        }
    },

    _get_scalable_cursor_needed: function() {
        var h = this.currently_attached_to.height;
        var w = this.currently_attached_to.width;

        var cursor_position = this.cursor_wall.get_position();

        var vertical_percentage = ((this.currently_attached_to.object3D.position.y + h / 2) - cursor_position.y ) / h;
        var horizontal_percentage = this.currently_attached_to.get_horizontal_distance_to_center(cursor_position.x, cursor_position.z) / w;

        var scroll_vertical   = false;
        var scroll_horizontal = false;

        if (vertical_percentage < 0.03 || vertical_percentage > 0.97) {
            scroll_vertical = true;
        }

        if (horizontal_percentage > .47) {
            scroll_horizontal = true;
        }

        var cursor_already_set = false;
        if (is_defined(this.currently_attached_to.block_input_on_inner_70_percent)) {
            if (this.currently_attached_to.block_input_on_inner_70_percent) {
                if (vertical_percentage > .3 && vertical_percentage < .7 && horizontal_percentage < .35) {
                    l('SET CURSOR TO CSS');
                    this._set_current_cursor(CURSOR_TYPE_CSS);
                }
            }
        }

        if (!cursor_already_set) {
            if (scroll_horizontal && scroll_vertical) {
                this._set_current_cursor(CURSOR_TYPE_LARGER);
            } else if (scroll_vertical) {
                this._set_current_cursor(CURSOR_TYPE_VERTICAL);
            } else if (scroll_horizontal) {
                this._set_current_cursor(CURSOR_TYPE_HORIZONTAL);
            } else {
                this._set_current_cursor(CURSOR_TYPE_MOUSE);
            }
        }
    },

    _set_current_cursor: function(cursor) {
        if (this._current_cursor !== this._cursors[cursor]) {
            this._previous_cursor = this._current_cursor;
            if (is_defined(this._previous_cursor)) {
                this._previous_cursor.set_to_invisible();
            }
            this._current_cursor = this._cursors[cursor];
            this._current_cursor.set_to_visible();
        }
    },

    _is_current_cursor_type: function(cursor_type) {
        return this._current_cursor.get_text() === cursor_type;
    },

    /*        ___                      __        __
     | |\ | |  |  |  /\  |       |    /  \  /\  |  \
     | | \| |  |  | /~~\ |___    |___ \__/ /~~\ |__/ */
    create: function() {
        this.cursor_wall = new FloatingWall(16, 16, null, null, this.world, false);
        this.cursor_wall.set_to_invisible();
        var cursor_row = this.cursor_wall.add_row(0);
        this._create_cursor(CURSOR_TYPE_HORIZONTAL, cursor_row);
        this._create_cursor(CURSOR_TYPE_VERTICAL, cursor_row);
        this._create_cursor(CURSOR_TYPE_HAND, cursor_row);
        this._create_cursor(CURSOR_TYPE_POINTER, cursor_row);
        this._create_cursor(CURSOR_TYPE_LARGER, cursor_row);
        this._create_cursor(CURSOR_TYPE_MOUSE, cursor_row);
        this._create_cursor(CURSOR_TYPE_CSS, cursor_row);
    },

    _create_cursor: function(cursor_type, cursor_row) {
        var c = cursor_row.add_2D_element([0, 1], cursor_type, TYPE_ICON);
        c.set_attachment_depth_offset(6);
        c.set_attachment_horizontal_offset(8, null);
        c.set_attachment_vertical_offset(-8, null);
        c.set_to_invisible();

        // TODO : Transparency issues still exist.
        // Needed for fixing transparency issues. This has the cursor be rendered last.
        c.mesh.renderDepth = -1;

        this._cursors[cursor_type] = c;
    }

};<|endoftext|>


<commit_before>/* global describe, it, xit */
/* jslint node: true, esnext: true */

'use strict';

const chai = require('chai'),
  assert = chai.assert,
  expect = chai.expect,
  should = chai.should();

const createGrammar = require('../lib/grammar').createGrammar;

describe('mini_lang',
  function () {
    function Value(value) {
      return Object.create(null, {
        value: {
          value: value
        }
      });
    }

    const identifiers = {
      array: [1, 2, 3, 4, 5, 6, 7]
    };

    const functions = {
      concat: args => Value(args.map(a => a.value).join('')),
      noargs: args => Value('-- no args --'),
      onearg: args => args[0]
    };

    let myGrammar = createGrammar({
      identifier(value, properties, context) {
          if (functions[value]) {
            properties.type.value = 'function';
            properties.value.value = functions[value];
          } else if (identifiers[value]) {
            properties.value.value = identifiers[value];
          }
          //console.log(`create identifier: ${value} ${context}`);
        },
        prefix: {
          '(': {
            precedence: 80,
            led(grammar, left) {
              if (left.type === 'function') {
                const args = [];

                if (grammar.token.value !== ')') {
                  while (true) {
                    args.push(grammar.expression(0));

                    if (grammar.token.value !== ',') {
                      break;
                    }
                    grammar.advance(',');
                  }
                }

                grammar.advance(')');

                return left.value(args);
              } else {
                const e = grammar.expression(0);
                grammar.advance(')');
                return e;
              }
            }
          }
        },
        infix: {
          ',': {},
          ')': {},
          ']': {},
          '[': {
            precedence: 40,
            led(grammar, left) {
              const right = grammar.expression(0);
              grammar.advance(']');
              return Value(left.value[right.value]);
            }
          },
          '+': {
            precedence: 50,
            combine: (left, right) => Value(left.value + right.value)
          },
          '-': {
            precedence: 50,
            combine: (left, right) => Value(left.value - right.value)
          },
          '*': {
            precedence: 60,
            combine: (left, right) => Value(left.value * right.value)
          },
          '/': {
            precedence: 60,
            combine: (left, right) => Value(left.value / right.value)
          }
        }
    });

    it('evaluates array', () => assert.equal(myGrammar.parse('array[3 * 2] + 2').value, 9));
    it('evaluates function no args', () => assert.equal(myGrammar.parse('noargs()', 'norags context').value,
      '-- no args --'));
    it('evaluates function one arg', () => assert.equal(myGrammar.parse('onearg("the arg")').value, 'the arg'));
    it('evaluates function', () => assert.equal(myGrammar.parse('concat("A","B")').value, 'AB'));
  });
<commit_msg>test: check more complicated function arguments too<commit_after>/* global describe, it, xit */
/* jslint node: true, esnext: true */

'use strict';

const chai = require('chai'),
  assert = chai.assert,
  expect = chai.expect,
  should = chai.should();

const createGrammar = require('../lib/grammar').createGrammar;

describe('mini_lang',
  function () {
    function Value(value) {
      return Object.create(null, {
        value: {
          value: value
        }
      });
    }

    const identifiers = {
      array: [1, 2, 3, 4, 5, 6, 7]
    };

    const functions = {
      concat: args => Value(args.map(a => a.value).join('')),
      noargs: args => Value('-- no args --'),
      onearg: args => args[0]
    };

    let myGrammar = createGrammar({
      identifier(value, properties, context) {
          if (functions[value]) {
            properties.type.value = 'function';
            properties.value.value = functions[value];
          } else if (identifiers[value]) {
            properties.value.value = identifiers[value];
          }
          //console.log(`create identifier: ${value} ${context}`);
        },
        prefix: {
          '(': {
            precedence: 80,
            led(grammar, left) {
              if (left.type === 'function') {
                const args = [];

                if (grammar.token.value !== ')') {
                  while (true) {
                    args.push(grammar.expression(0));

                    if (grammar.token.value !== ',') {
                      break;
                    }
                    grammar.advance(',');
                  }
                }

                grammar.advance(')');

                return left.value(args);
              } else {
                const e = grammar.expression(0);
                grammar.advance(')');
                return e;
              }
            }
          }
        },
        infix: {
          ',': {},
          ')': {},
          ']': {},
          '[': {
            precedence: 40,
            led(grammar, left) {
              const right = grammar.expression(0);
              grammar.advance(']');
              return Value(left.value[right.value]);
            }
          },
          '+': {
            precedence: 50,
            combine: (left, right) => Value(left.value + right.value)
          },
          '-': {
            precedence: 50,
            combine: (left, right) => Value(left.value - right.value)
          },
          '*': {
            precedence: 60,
            combine: (left, right) => Value(left.value * right.value)
          },
          '/': {
            precedence: 60,
            combine: (left, right) => Value(left.value / right.value)
          }
        }
    });

    it('evaluates array', () => assert.equal(myGrammar.parse('array[3 * 2] + 2').value, 9));
    it('evaluates function no args', () => assert.equal(myGrammar.parse('noargs()', 'norags context').value,
      '-- no args --'));
    it('evaluates function one arg', () => assert.equal(myGrammar.parse('onearg("the arg")').value, 'the arg'));
    it('evaluates function', () => assert.equal(myGrammar.parse('concat("A","B")').value, 'AB'));
    it('evaluates function 2', () => assert.equal(myGrammar.parse('concat(concat("A","B"),"C")').value, 'ABC'));
  });
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var async = require('async');

module.exports = function(options){
  var seneca = this;
  var plugin = 'cd-users';
  var ENTITY_NS = 'sys/user';

  seneca.add({role: plugin, cmd: 'list'}, cmd_list);
  seneca.add({role: plugin, cmd: 'register'}, cmd_register);
  seneca.add({role: plugin, cmd: 'promote'}, cmd_promote);
  seneca.add({role: plugin, cmd: 'get_users_by_emails'}, cmd_get_users_by_emails);

  function cmd_list(args, done){
    var seneca = this;

    async.waterfall([
      function(done) {
        seneca.make(ENTITY_NS).list$({ids: args.ids}, done);
      },
      function(users, done) {
        return done(null, _.map(users, function (user) {
          return user.data$();
        }));
      }
    ], done);
  }

  function cmd_register(args, done) {
    //Roles Available: basic-user, mentor, champion, cdf-admin
    var seneca = this;
    args.roles = ['basic-user'];
    args.mailingList = (args.mailingList) ? 1 : 0;
    seneca.act({role:'user', cmd:'register'}, args, function(err, response) {
      if(err) return done(err);
      done(null, response);
    });
  }

  function cmd_promote(args, done) {
    var seneca = this;
    var newRoles = args.roles;
    var userId = args.id;
    var userEntity = seneca.make$(ENTITY_NS);

    userEntity.load$(userId, function(err, response) {
      if(err) return done(err);
      var user = response;
      _.each(newRoles, function(newRole) {
        user.roles.push(newRole);
      });
      user.roles = _.uniq(user.roles);
      userEntity.save$(user, function(err, response) {
        if(err) return done(err);
        done(null, response);
      });
    });

  }

  function cmd_get_users_by_emails(args, done){
    var seneca = this, query = {};

    query.email = new RegExp(args.email);
    query.limit$ = query.limit$ ? query.limit$ : 10;

    seneca.make(ENTITY_NS).list$(query, function(err, users){
      if(err){
        return done(err);
      }

      users = _.map(users, function(user){
        return {email: user.email, id: user.id};
      });

      users = _.uniq(users, 'email');

      done(null, users);
    });
  }

  return {
    name: plugin
  };
};<commit_msg>Make email search case insensitive<commit_after>'use strict';

var _ = require('lodash');
var async = require('async');

module.exports = function(options){
  var seneca = this;
  var plugin = 'cd-users';
  var ENTITY_NS = 'sys/user';

  seneca.add({role: plugin, cmd: 'list'}, cmd_list);
  seneca.add({role: plugin, cmd: 'register'}, cmd_register);
  seneca.add({role: plugin, cmd: 'promote'}, cmd_promote);
  seneca.add({role: plugin, cmd: 'get_users_by_emails'}, cmd_get_users_by_emails);

  function cmd_list(args, done){
    var seneca = this;

    async.waterfall([
      function(done) {
        seneca.make(ENTITY_NS).list$({ids: args.ids}, done);
      },
      function(users, done) {
        return done(null, _.map(users, function (user) {
          return user.data$();
        }));
      }
    ], done);
  }

  function cmd_register(args, done) {
    //Roles Available: basic-user, mentor, champion, cdf-admin
    var seneca = this;
    args.roles = ['basic-user'];
    args.mailingList = (args.mailingList) ? 1 : 0;
    seneca.act({role:'user', cmd:'register'}, args, function(err, response) {
      if(err) return done(err);
      done(null, response);
    });
  }

  function cmd_promote(args, done) {
    var seneca = this;
    var newRoles = args.roles;
    var userId = args.id;
    var userEntity = seneca.make$(ENTITY_NS);

    userEntity.load$(userId, function(err, response) {
      if(err) return done(err);
      var user = response;
      _.each(newRoles, function(newRole) {
        user.roles.push(newRole);
      });
      user.roles = _.uniq(user.roles);
      userEntity.save$(user, function(err, response) {
        if(err) return done(err);
        done(null, response);
      });
    });

  }

  function cmd_get_users_by_emails(args, done){
    var seneca = this, query = {};

    query.email = new RegExp(args.email, 'i');
    query.limit$ = query.limit$ ? query.limit$ : 10;

    seneca.make(ENTITY_NS).list$(query, function(err, users){
      if(err){
        return done(err);
      }

      users = _.map(users, function(user){
        return {email: user.email, id: user.id};
      });

      users = _.uniq(users, 'email');

      done(null, users);
    });
  }

  return {
    name: plugin
  };
};
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright (c) 2012 Adtec Productions, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **************************************************************************/

var
_fs = require('fs'),
_https = require('https'),
_mime = require('mime'),
_req = require('request'),
_URL = 'build.phonegap.com'

encodeFieldHeader = function(boundary, name, value){
   var result = [
     "--" + boundary + "\r\n",
     "Content-Disposition: form-data; name=\"",
     "" + name + "\"\r\n\r\n" + value + "\r\n",
     ].join("");
   return result;
},

encodeFileHeader = function(boundary, type, name, filename){
   var result = [
      "--" + boundary + "\r\n",
      "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n",
      "Content-Type: " + type + "\r\n\r\n"
     ].join("");
   return result;
},

postMultipart = function(token, postData, boundary, apiCall, callback){
   var
   len = 0, i=0,
   options = null,
   request = null;

   for( i=0; i<postData.length; i++){
      len += postData[i].length;
   }

   options = {
      host: _URL,
      path: '/api/v1/' + apiCall + '?auth_token='+token,
      method: 'POST',
      headers:{
         'Content-Type': 'multipart/form-data; boundary=' + boundary,
         'Content-Length': len
      }
   };

   request = _https.request(options, function(response){
      var replyData = '';
      response.on('data', function(chunk){
         replyData+= chunk;
      });
      response.on('end', function(){
         callback.success(replyData);
      });
      response.on('error', function(e){
         callback.erro(e.message);
      });
   });

   for( i=0; i<postData.length; i++){
      request.write(postData[i]);
   }
   request.end();

},

initMultipartUpload = function(token, inputFile, reqData, apiCall, callback){
   var
   boundary = 'bound' + Math.random(),
   postData = [],
   fileReader = null,
   fileContents = '';

   postData.push(new Buffer(encodeFieldHeader(boundary, "data", JSON.stringify(reqData)), 'ascii'));
   postData.push(new Buffer(encodeFileHeader(boundary, _mime.lookup(inputFile), "file", inputFile), 'ascii'));
   fileReader = _fs.createReadStream(inputFile, {encoding: 'binary'});
   fileReader.on('data', function(fileData){ fileContents+= fileData;});
   fileReader.on('end', function(){
      postData.push(new Buffer(fileContents, 'binary'));
      postData.push(new Buffer("\r\n--" + boundary + "--", 'ascii'));
      postMultipart(token, postData, boundary, apiCall, callback);
      });
},

 /******************************************************************
 *
 *  Create a new File-based App.
 *
 *  Required parameters
 *
 *  title: You must specify a title for your app - if a title is also
 *         specified in a config.xml in your package, the one in the
 *         config.xml file will take preference.
 *  create_method: use "file" for this method
 *
 *  Optional parameters
 *
 *  package: Sets the package identifier for your app. This can also be
 *           done after creation, or in your config.xml file.
 *           Defaults to com.phonegap.www
 *  version: Sets the version of your app. This can also be done after
 *           creation, or in your config.xml file. Defaults to 0.0.1
 *  description: Sets the description for your app. This can also be
 *               done after creation, or in your config.xml file.
 *               Defaults to empty.
 *  debug: Builds your app in debug mode. Defaults to false.
 *  keys: Set the signing keys to use for each platform you wish to sign.
 *  private: Whether your app can be publicly downloaded. Defaults to
 *           true during beta period; will default to false once the
 *           beta period is complete
 *  phonegap_version: Which version of PhoneGap your app uses. See
 *                    config.xml for details on which are supported,
 *                    and which one is currently the default
 *
 * File-backed applications
 *
 * To create a file-backed application, set the create_method parameter
 * to file, and include a zip file, a tar.gz file, or an index.html
 * file in the multipart body of your post, with the parameter name file.
 *
 *    POST https://build.phonegap.com/api/v1/apps
 *****************************************************************/
createFileBasedApp = function(token, inputFile, dataObj, callback){
   initMultipartUpload(token, inputFile, dataObj, 'apps', callback);
},

/******************************************************************
 *    Updating a file-based application
 *
 *    If the application has been created from a file upload, you
 *    can include a new index.html, zip file, or tar.gz file as the
 *    file parameter in your request to update the contents.
 *
 *    PUT https://build.phonegap.com/api/v1/apps/:id
 *****************************************************************/
updateFileBasedApp = function(token, inputFile, appId, callback){
   _req = require('request'),
   apiPath = '/api/v1/apps/' + appId + '?auth_token=' + token;
   _fs.createReadStream(inputFile).pipe(req.put('https://build.phonegap.com' + apiPath))
   .on('error', function(e){callback.error(e.message);})
   .on('close', callback.success);

},

createToken = function(rawCredentials, callback){
   var
   auth = "Basic " + new Buffer(rawCredentials).toString("base64"),
   options = {
      url : 'https://'+_URL+"/token",
      headers : { "Authorization" : auth }
      };

   _req.post(options, function (error, response, body) {
      if((error!==null) || (response.statusCode!=200))
         {
         var errStr = "AJAX error.  Your request could not be completed. Please verify your login credentials and network access.  statusCode: " + response.statusCode;
         if(JSON.parse(body) && JSON.parse(body).error){errStr +="\n" + JSON.parse(body).error;}
         callback.error(errStr);
         return;
         }
      callback.success(JSON.parse(body).token);
      });
}
;

module.exports = {
   createFileBasedApp:createFileBasedApp,
   updateFileBasedApp:updateFileBasedApp,
   createAuthToken: createToken,
};<commit_msg>typo fix. No functional changes.<commit_after>/**************************************************************************
 * Copyright (c) 2012 Adtec Productions, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **************************************************************************/

var
_fs = require('fs'),
_https = require('https'),
_mime = require('mime'),
_req = require('request'),
_URL = 'build.phonegap.com'

encodeFieldHeader = function(boundary, name, value){
   var result = [
     "--" + boundary + "\r\n",
     "Content-Disposition: form-data; name=\"",
     "" + name + "\"\r\n\r\n" + value + "\r\n",
     ].join("");
   return result;
},

encodeFileHeader = function(boundary, type, name, filename){
   var result = [
      "--" + boundary + "\r\n",
      "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n",
      "Content-Type: " + type + "\r\n\r\n"
     ].join("");
   return result;
},

postMultipart = function(token, postData, boundary, apiCall, callback){
   var
   len = 0, i=0,
   options = null,
   request = null;

   for( i=0; i<postData.length; i++){
      len += postData[i].length;
   }

   options = {
      host: _URL,
      path: '/api/v1/' + apiCall + '?auth_token='+token,
      method: 'POST',
      headers:{
         'Content-Type': 'multipart/form-data; boundary=' + boundary,
         'Content-Length': len
      }
   };

   request = _https.request(options, function(response){
      var replyData = '';
      response.on('data', function(chunk){
         replyData+= chunk;
      });
      response.on('end', function(){
         callback.success(replyData);
      });
      response.on('error', function(e){
         callback.error(e.message);
      });
   });

   for( i=0; i<postData.length; i++){
      request.write(postData[i]);
   }
   request.end();

},

initMultipartUpload = function(token, inputFile, reqData, apiCall, callback){
   var
   boundary = 'bound' + Math.random(),
   postData = [],
   fileReader = null,
   fileContents = '';

   postData.push(new Buffer(encodeFieldHeader(boundary, "data", JSON.stringify(reqData)), 'ascii'));
   postData.push(new Buffer(encodeFileHeader(boundary, _mime.lookup(inputFile), "file", inputFile), 'ascii'));
   fileReader = _fs.createReadStream(inputFile, {encoding: 'binary'});
   fileReader.on('data', function(fileData){ fileContents+= fileData;});
   fileReader.on('end', function(){
      postData.push(new Buffer(fileContents, 'binary'));
      postData.push(new Buffer("\r\n--" + boundary + "--", 'ascii'));
      postMultipart(token, postData, boundary, apiCall, callback);
      });
},

 /******************************************************************
 *
 *  Create a new File-based App.
 *
 *  Required parameters
 *
 *  title: You must specify a title for your app - if a title is also
 *         specified in a config.xml in your package, the one in the
 *         config.xml file will take preference.
 *  create_method: use "file" for this method
 *
 *  Optional parameters
 *
 *  package: Sets the package identifier for your app. This can also be
 *           done after creation, or in your config.xml file.
 *           Defaults to com.phonegap.www
 *  version: Sets the version of your app. This can also be done after
 *           creation, or in your config.xml file. Defaults to 0.0.1
 *  description: Sets the description for your app. This can also be
 *               done after creation, or in your config.xml file.
 *               Defaults to empty.
 *  debug: Builds your app in debug mode. Defaults to false.
 *  keys: Set the signing keys to use for each platform you wish to sign.
 *  private: Whether your app can be publicly downloaded. Defaults to
 *           true during beta period; will default to false once the
 *           beta period is complete
 *  phonegap_version: Which version of PhoneGap your app uses. See
 *                    config.xml for details on which are supported,
 *                    and which one is currently the default
 *
 * File-backed applications
 *
 * To create a file-backed application, set the create_method parameter
 * to file, and include a zip file, a tar.gz file, or an index.html
 * file in the multipart body of your post, with the parameter name file.
 *
 *    POST https://build.phonegap.com/api/v1/apps
 *****************************************************************/
createFileBasedApp = function(token, inputFile, dataObj, callback){
   initMultipartUpload(token, inputFile, dataObj, 'apps', callback);
},

/******************************************************************
 *    Updating a file-based application
 *
 *    If the application has been created from a file upload, you
 *    can include a new index.html, zip file, or tar.gz file as the
 *    file parameter in your request to update the contents.
 *
 *    PUT https://build.phonegap.com/api/v1/apps/:id
 *****************************************************************/
updateFileBasedApp = function(token, inputFile, appId, callback){
   _req = require('request'),
   apiPath = '/api/v1/apps/' + appId + '?auth_token=' + token;
   _fs.createReadStream(inputFile).pipe(req.put('https://build.phonegap.com' + apiPath))
   .on('error', function(e){callback.error(e.message);})
   .on('close', callback.success);

},

createToken = function(rawCredentials, callback){
   var
   auth = "Basic " + new Buffer(rawCredentials).toString("base64"),
   options = {
      url : 'https://'+_URL+"/token",
      headers : { "Authorization" : auth }
      };

   _req.post(options, function (error, response, body) {
      if((error!==null) || (response.statusCode!=200))
         {
         var errStr = "AJAX error.  Your request could not be completed. Please verify your login credentials and network access.  statusCode: " + response.statusCode;
         if(JSON.parse(body) && JSON.parse(body).error){errStr +="\n" + JSON.parse(body).error;}
         callback.error(errStr);
         return;
         }
      callback.success(JSON.parse(body).token);
      });
}
;

module.exports = {
   createFileBasedApp:createFileBasedApp,
   updateFileBasedApp:updateFileBasedApp,
   createAuthToken: createToken,
};<|endoftext|>


<commit_before>'use strict';

var moar = require('../lib/moar.js'),
    _ = require('lodash');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports['moar'] = {
  setUp: function(done) {
    // setup here
    done();
  },
  'functions': function(test) {
    test.expect(2);
    // tests here
    test.ok(typeof moar.get === "function", 'should define a get function.');
    test.ok(typeof moar.search === "function", 'should define a search function.');
    test.done();
  },
  'get': function(test) {
    test.expect(1);
    moar.get({"board": "v"}, function(data) {
      test.ok(_(data).some(function(el) { return el.threads; }) || data.error ,"should make a request to 4chan\'s API, sending either a json response or an error object to the callback.");
      test.done();
    });
  },
  'get exceptions': function(test) {
    test.expect(4);
    test.throws(moar.get(), "ArgumentError: Wrong number of arguments: 0 for [1 - 2].", "should throw errors on incorrect usage. (too few arguments)");
    test.throws(moar.get("one", "two", "three"), "ArgumentError: Wrong number of arguments: 3 for [1 - 2].", "should throw errors on incorrect usage. (too many arguments)");
    test.throws(moar.get("wrong", "arguments"), "ArgumentError: Incorrect usage.", "should throw errors on incorrect usage. (wrong type for callback)");
    test.throws(moar.get(function() { console.log("hi");}, function(data) { return data; }), "ArgumentError: Incorrect usage.", "should throw errors on incorrect usage. (wrong type for options)");
    test.done();
  },
  'search': function(test) {
    test.expect(1);
    moar.search({"board": "v", "term": "pokemon"}, function(data) {
      test.ok(data, "should make a request to 4chan\'s API, sending either a json response or an error object to the callback.");
      test.done();
    });
  }
};
<commit_msg>Wrap test.throws calls in anonymous functions for correct usage<commit_after>'use strict';

var moar = require('../lib/moar.js'),
    _ = require('lodash');

/*
  ======== A Handy Little Nodeunit Reference ========
  https://github.com/caolan/nodeunit

  Test methods:
    test.expect(numAssertions)
    test.done()
  Test assertions:
    test.ok(value, [message])
    test.equal(actual, expected, [message])
    test.notEqual(actual, expected, [message])
    test.deepEqual(actual, expected, [message])
    test.notDeepEqual(actual, expected, [message])
    test.strictEqual(actual, expected, [message])
    test.notStrictEqual(actual, expected, [message])
    test.throws(block, [error], [message])
    test.doesNotThrow(block, [error], [message])
    test.ifError(value)
*/

exports['moar'] = {
  setUp: function(done) {
    // setup here
    done();
  },
  'functions': function(test) {
    test.expect(2);
    // tests here
    test.ok(typeof moar.get === "function", 'should define a get function.');
    test.ok(typeof moar.search === "function", 'should define a search function.');
    test.done();
  },
  'get': function(test) {
    test.expect(1);
    moar.get({"board": "v"}, function(error, data) {
      test.ok(_(data).some(function(el) { return el.threads; }) || error ,"should make a request to 4chan\'s API, sending either a json response or an error object to the callback.");
      test.done();
    });
  },
  'get exceptions': function(test) {
    test.expect(4);
    test.throws(function() { moar.get(); }, "should throw errors on incorrect usage. (too few arguments)");
    test.throws(function() { moar.get("one", "two", "three"); }, "should throw errors on incorrect usage. (too many arguments)");
    test.throws(function() { moar.get("wrong", "arguments"); }, "should throw errors on incorrect usage. (wrong type for callback)");
    test.throws(function() { moar.get(function() { console.log("hi");}, function(data) { return data; }); }, "should throw errors on incorrect usage. (wrong type for options)");
    test.done();
  },
  'search': function(test) {
    test.expect(1);
    moar.search({"board": "v", "term": "pokemon"}, function(data) {
      test.ok(data, "should make a request to 4chan\'s API, sending either a json response or an error object to the callback.");
      test.done();
    });
  }
};
<|endoftext|>


<commit_before>import { logger } from "./logger";

import { TopicObserver } from "./topic-observer";

export class AppClient extends TopicObserver {

    /**
     *
     * @param {MqttClient} mqttClient
     * @param httpClient
     */
    constructor(mqttClient, httpClient) {

        // call super
        super();

        // mqtt client
        this.mqttClient = mqttClient;
        this.event_observer = null;

        // register observer
        this.event_observer = (event) => this._eventHandler(event);
        this.mqttClient.observe(this.event_observer);

        // http client
        this.httpClient = httpClient;

        // initialize
        this.ambulances = undefined;
        this.hospitals = undefined;
        this.bases = undefined;
        this.calls = undefined;

        // observer methods
        this.updateAmbulance = (message) => { this._updateAmbulance(message) };
        this.updateHospital = (message) => { this._updateHospital(message) };
        this.updateCall = (message) => { this._updateCall(message) };
        this.updateAmbulanceCallStatus = (message) => { this._updateAmbulanceCallStatus(message) };

    }

    disconnect() {

        // return if not connected
        if (!this.mqttClient.isConnected)
            return;

        // remove observer
        this.mqttClient.remove(this.event_observer);
        this.event_observer = null;

        // connect to client
        this.mqttClient.disconnect();

    }

    publish(topic, payload, qos, retained) {
        this.mqttClient.publish(topic, payload, qos, retained);
    }

    retrieveAmbulances() {

        // initialized if needed
        if (typeof this.ambulances === 'undefined')
            this.ambulances = {};

        // retrieve ambulances
        return this.httpClient.get('ambulance/')
            .then( (response) => {

                // Update ambulances
                response.data.forEach( (ambulance) => {

                    // update ambulance
                    this.ambulances[ambulance.id] = ambulance;

                    // subscribe
                    // TODO: check if already subscribed
                    this._subscribe('ambulance/' + ambulance.id + '/data',
                        this.updateAmbulance);
                    this._subscribe('ambulance/' + ambulance.id + '/call/+/status',
                        this.updateAmbulanceCallStatus);

                });

                // return ambulances
                return this.ambulances;

            })

    }

    retrieveHospitals() {

        // initialized if needed
        if (typeof this.hospitals === 'undefined')
            this.hospitals = {};

        // retrieve ambulances
        return this.httpClient.get('hospital/')
            .then( (response) => {

                // Update hospitals
                response.data.forEach( (hospital) => {

                    // update hospital
                    this.hospitals[hospital.id] = hospital;

                    // subscribe
                    // TODO: check if already subscribed
                    this._subscribe('hospital/' + hospital.id + '/data',
                        this.updateHospital);

                });

                // return hospitals
                return this.hospitals;

            })

    }

    retrieveCalls() {

        // initialized if needed
        if (typeof this.calls === 'undefined')
            this.calls = {};

        // retrieve ambulances
        return this.httpClient.get('call/')
            .then( (response) => {

                // Update calls
                response.data.forEach( (call) => {

                    // update call
                    this.calls[call.id] = call;

                    // subscribe
                    // TODO: check if already subscribed
                    this._subscribe('call/' + call.id + '/data',
                        this.updateCall);

                });

                // return calls
                return this.calls;

            })

    }

    retrieveBases() {

        // initialized if needed
        if (typeof this.bases === 'undefined')
            this.bases = {};

        // retrieve ambulances
        return this.httpClient.get('location/Base/')
            .then( (response) => {

                // Update bases
                response.data.forEach( (base) => {

                    // update base
                    this.bases[base.id] = base;

                });

                // return bases
                return this.bases;

            })

    }

    // private methods

    // subscribe methods

    _subscribe(filter, fn, options = {qos:2}) {
        logger.log('debug', "subscribing to '%s'", filter);
        this.mqttClient.subscribe(filter, options);
        this.observe(filter, fn)
    }

    _unsubscribe(filter, fn, options = {}) {
        logger.log('debug', "unsubscribing from '%s'", filter);
        this.mqttClient.unsubscribe(filter, options);
        this.remove(filter, fn)
    }

    // observer methods

    _updateAmbulance(message) {
        const ambulance = message.payload;
        this.ambulances[ambulance.id] = ambulance;
    }

    _updateHospital(message) {
        const hospital = message.payload;
        this.hospitals[hospital.id] = hospital;
    }

    _updateCall(message) {
        const call = message.payload;

        if (call.status === 'E')
        // call ended? remove
            this._removeCall(call);
        else
        // update
            this.call[call.id] = call;
    }

    _updateAmbulanceCallStatus(message) {
        const status = message.payload;

        // get ambulance and call ids
        const topic = message.topic.split('/');
        const ambulance_id = topic[1];
        const call_id = topic[3];

        // is this a new call?
        logger.log('debug', "Received ambulance '%d' call '%d' status '%s'", ambulance_id, call_id, status);
        if ( !this.calls.hasOwnProperty(call_id) && status !== 'C' ) {

            logger.log('debug', "Retrieving call '%d'", call_id);

            const pause = (duration) => new Promise(res => setTimeout(res, duration));
            const backoff = (retries, fn, delay = 500) =>
                fn().catch(err => retries > 1
                    ? pause(delay).then(() => backoff(retries - 1, fn, delay * 2))
                    : Promise.reject(err));

            // retrieve call from api
            backoff(3, () => this.httpClient.get('call/' + call_id + '/'), 500)
                .then( (response) => {

                    logger.log('debug', "Retrieved call '%j'", call);

                    // add call
                    this._addCall(response.data);

                })
                .catch( (error) => {
                    logger.log('error', "Could not retrieve call with id '%d': '%s'", call_id, error);
                });

        }

    }

    _addCall(call) {

        // update call
        this.calls[call.id] = call;

        // subscribe
        this._subscribe('call/' + call.id + '/data', this.updateCall);

    }

    _removeCall(call) {

        // unsubscribe call
        this._unsubscribe('call/' + call.id + '/data', this.updateCall);

        // delete call
        delete this.calls[call.id];

    }

    // mqtt event handler

    /**
     *
     * @param {MqttEvent} event
     */
    _eventHandler(event) {

        logger.log('debug', "event: '%j'", event);

        if (event.event === 'messageArrived') {

            const topic = event.object.destinationName;
            let payload;
            try {
                payload = JSON.parse(event.object.payloadString);
            } catch(e) {
                logger.warn('Could not parse incoming message.');
                payload = event.object.payloadString;
            }

            // broadcast
            logger.log('debug', "message: '%j'", {topic: topic, payload: payload});
            this.broadcast(topic, {topic: topic, payload: payload});

        } else if (event.event === 'messageSent') {
            /* ignore */
        } else if (event.event === 'connect') {
            /* ignore */
        } else if (event.event === 'lostConnection') {
            /* ignore */
        } else
            logger.log('warn', "Unknown event type '%s'", event.event);

    }

}
<commit_msg>Adapting map.js to base.js.<commit_after>import { logger } from "./logger";

import { TopicObserver } from "./topic-observer";

export class AppClient extends TopicObserver {

    /**
     *
     * @param {MqttClient} mqttClient
     * @param httpClient
     */
    constructor(mqttClient, httpClient) {

        // call super
        super();

        // mqtt client
        this.mqttClient = mqttClient;
        this.event_observer = null;

        // register observer
        this.event_observer = (event) => this._eventHandler(event);
        this.mqttClient.observe(this.event_observer);

        // http client
        this.httpClient = httpClient;

        // initialize
        this.ambulances = undefined;
        this.hospitals = undefined;
        this.bases = undefined;
        this.calls = undefined;

        // observer methods
        this.updateAmbulance = (message) => { this._updateAmbulance(message) };
        this.updateHospital = (message) => { this._updateHospital(message) };
        this.updateCall = (message) => { this._updateCall(message) };
        this.updateAmbulanceCallStatus = (message) => { this._updateAmbulanceCallStatus(message) };

    }

    disconnect() {

        // return if not connected
        if (!this.mqttClient.isConnected)
            return;

        // remove observer
        this.mqttClient.remove(this.event_observer);
        this.event_observer = null;

        // connect to client
        this.mqttClient.disconnect();

    }

    publish(topic, payload, qos, retained) {
        this.mqttClient.publish(topic, payload, qos, retained);
    }

    retrieveAmbulances() {

        // initialized if needed
        if (typeof this.ambulances === 'undefined')
            this.ambulances = {};

        // retrieve ambulances
        return this.httpClient.get('ambulance/')
            .then( (response) => {

                // Update ambulances
                response.data.forEach( (ambulance) => {

                    // update ambulance
                    this.ambulances[ambulance.id] = ambulance;

                    // subscribe
                    // TODO: check if already subscribed
                    this._subscribe('ambulance/' + ambulance.id + '/data',
                        this.updateAmbulance);
                    this._subscribe('ambulance/' + ambulance.id + '/call/+/status',
                        this.updateAmbulanceCallStatus);

                });

                // return ambulances
                return this.ambulances;

            })

    }

    retrieveHospitals() {

        // initialized if needed
        if (typeof this.hospitals === 'undefined')
            this.hospitals = {};

        // retrieve ambulances
        return this.httpClient.get('hospital/')
            .then( (response) => {

                // Update hospitals
                response.data.forEach( (hospital) => {

                    // update hospital
                    this.hospitals[hospital.id] = hospital;

                    // subscribe
                    // TODO: check if already subscribed
                    this._subscribe('hospital/' + hospital.id + '/data',
                        this.updateHospital);

                });

                // return hospitals
                return this.hospitals;

            })

    }

    retrieveCalls() {

        // initialized if needed
        if (typeof this.calls === 'undefined')
            this.calls = {};

        // retrieve ambulances
        return this.httpClient.get('call/')
            .then( (response) => {

                // Update calls
                response.data.forEach( (call) => {

                    // update call
                    this.calls[call.id] = call;

                    // subscribe
                    // TODO: check if already subscribed
                    this._subscribe('call/' + call.id + '/data',
                        this.updateCall);

                });

                // return calls
                return this.calls;

            })

    }

    retrieveBases() {

        // initialized if needed
        if (typeof this.bases === 'undefined')
            this.bases = {};

        // retrieve ambulances
        return this.httpClient.get('location/Base/')
            .then( (response) => {

                // Update bases
                response.data.forEach( (base) => {

                    // update base
                    this.bases[base.id] = base;

                });

                // return bases
                return this.bases;

            })

    }

    // private methods

    // subscribe methods

    _subscribe(filter, fn, options = {qos:2}) {
        logger.log('debug', "subscribing to '%s'", filter);
        this.mqttClient.subscribe(filter, options);
        this.observe(filter, fn)
    }

    _unsubscribe(filter, fn, options = {}) {
        logger.log('debug', "unsubscribing from '%s'", filter);
        this.mqttClient.unsubscribe(filter, options);
        this.remove(filter, fn)
    }

    // observer methods

    _updateAmbulance(message) {
        const ambulance = message.payload;
        this.ambulances[ambulance.id] = ambulance;
    }

    _updateHospital(message) {
        const hospital = message.payload;
        this.hospitals[hospital.id] = hospital;
    }

    _updateCall(message) {
        const call = message.payload;

        if (call.status === 'E')
        // call ended? remove
            this._removeCall(call);
        else
        // update
            this.call[call.id] = call;
    }

    _updateAmbulanceCallStatus(message) {
        const status = message.payload;

        // get ambulance and call ids
        const topic = message.topic.split('/');
        const ambulance_id = topic[1];
        const call_id = topic[3];

        // is this a new call?
        logger.log('debug', "Received ambulance '%d' call '%d' status '%s'", ambulance_id, call_id, status);
        if ( !this.calls.hasOwnProperty(call_id) && status !== 'C' ) {

            logger.log('debug', "Retrieving call '%d'", call_id);

            // retrieve call from api
            this.httpClient.get('call/' + call_id + '/')
                .then( (response) => {

                    logger.log('debug', "Retrieved call '%j'", response.data);

                    // add call
                    this._addCall(response.data);

                })
                .catch( (error) => {
                    logger.log('error', "Could not retrieve call with id '%d': '%s'", call_id, error);
                });

        }

    }

    _addCall(call) {

        // update call
        this.calls[call.id] = call;

        // subscribe
        this._subscribe('call/' + call.id + '/data', this.updateCall);

    }

    _removeCall(call) {

        // unsubscribe call
        this._unsubscribe('call/' + call.id + '/data', this.updateCall);

        // delete call
        delete this.calls[call.id];

    }

    // mqtt event handler

    /**
     *
     * @param {MqttEvent} event
     */
    _eventHandler(event) {

        logger.log('debug', "event: '%j'", event);

        if (event.event === 'messageArrived') {

            const topic = event.object.destinationName;
            let payload;
            try {
                payload = JSON.parse(event.object.payloadString);
            } catch(e) {
                logger.warn('Could not parse incoming message.');
                payload = event.object.payloadString;
            }

            // broadcast
            logger.log('debug', "message: '%j'", {topic: topic, payload: payload});
            this.broadcast(topic, {topic: topic, payload: payload});

        } else if (event.event === 'messageSent') {
            /* ignore */
        } else if (event.event === 'connect') {
            /* ignore */
        } else if (event.event === 'lostConnection') {
            /* ignore */
        } else
            logger.log('warn', "Unknown event type '%s'", event.event);

    }

}
<|endoftext|>


<commit_before>const calls = [{
  type: 'call',
  id: '1',
  uri: 'http://pbx.sipcentric.com/api/v1/customers/5/calls/1',
  parent: 'http://pbx.sipcentric.com/api/v1/customers/5',
  created: '2016-01-01T12:00:00Z',
  scope: 'DOMAIN',
  direction: 'IN',
  from: '03303303330',
  to: 'Nimvelo IVR <002>',
  callStarted: '2016-01-01T11:30:00Z',
  outcome: 'ANSWERED',
  duration: 1800,
  cost: 0.000,
  callId: 'ast02-1452095027.2374613',
  linkedId: 'ast02-1452095027.2374613',
  links: {
    recordings: 'https://pbx.sipcentric.com/api/v1/customers/5/recordings?linkedId=ast02-1452095027.2374613'
  }
}, {
  type: 'call',
  id: '2',
  uri: 'http://pbx.sipcentric.com/api/v1/customers/5/calls/2',
  parent: 'http://pbx.sipcentric.com/api/v1/customers/5',
  created: '2016-01-02T13:00:00Z',
  scope: 'DOMAIN',
  direction: 'OUT',
  from: 'Josh Farrant <006>',
  to: '01234567890',
  callStarted: '2016-01-02T12:59:24Z',
  outcome: 'NO_ANSWER',
  duration: 0,
  cost: 0.000,
  callId: 'ast02-1452094411.2373896',
  linkedId: 'ast02-1452094411.2373896',
  links: {
    recordings: 'https://pbx.sipcentric.com/api/v1/customers/5/recordings?linkedId=ast02-1452094411.2373896'
  }
}, {
  type: 'call',
  id: '3',
  uri: 'http://pbx.sipcentric.com/api/v1/customers/5/calls/3',
  parent: 'http://pbx.sipcentric.com/api/v1/customers/5',
  created: '2016-01-03T14:09:21Z',
  scope: 'DOMAIN',
  direction: 'IN',
  from: 'Josh Farrant <006>',
  to: '07777777777',
  callStarted: '2016-01-03T14:07:06Z',
  outcome: 'ANSWERED',
  duration: 122,
  cost: 0.183,
  callId: 'ast03-1452090857.6405603',
  linkedId: 'ast03-1452090840.6405562',
  links: {
    recordings: 'https://pbx.sipcentric.com/api/v1/customers/5/recordings?linkedId=ast02-1452095027.2374613'
  }
}];

module.exports = {

  singleObject: calls[2],

  listSingle: {
    totalItems: 1,
    pageSize: 20,
    page: 1,
    items: [calls[1]]
  },

  listMultiple: {
    totalItems: 3,
    pageSize: 20,
    page: 1,
    items: calls
  }
};
<commit_msg>Minor mock call data change<commit_after>const calls = [{
  type: 'call',
  id: '1',
  uri: 'http://pbx.sipcentric.com/api/v1/customers/5/calls/1',
  parent: 'http://pbx.sipcentric.com/api/v1/customers/5',
  created: '2016-01-01T12:00:00Z',
  scope: 'DOMAIN',
  direction: 'IN',
  from: '03303303330',
  to: 'Nimvelo IVR <002>',
  callStarted: '2016-01-01T11:30:00Z',
  outcome: 'ANSWERED',
  duration: 1800,
  cost: 0.000,
  callId: 'ast02-1452095027.2374613',
  linkedId: 'ast02-1452095027.2374613',
  links: {
    recordings: 'https://pbx.sipcentric.com/api/v1/customers/5/recordings?linkedId=ast02-1452095027.2374613'
  }
}, {
  type: 'call',
  id: '2',
  uri: 'http://pbx.sipcentric.com/api/v1/customers/5/calls/2',
  parent: 'http://pbx.sipcentric.com/api/v1/customers/5',
  created: '2016-01-02T13:00:00Z',
  scope: 'DOMAIN',
  direction: 'OUT',
  from: 'Josh Farrant <106>',
  to: '01234567890',
  callStarted: '2016-01-02T12:59:24Z',
  outcome: 'NO_ANSWER',
  duration: 0,
  cost: 0.000,
  callId: 'ast02-1452094411.2373896',
  linkedId: 'ast02-1452094411.2373896',
  links: {
    recordings: 'https://pbx.sipcentric.com/api/v1/customers/5/recordings?linkedId=ast02-1452094411.2373896'
  }
}, {
  type: 'call',
  id: '3',
  uri: 'http://pbx.sipcentric.com/api/v1/customers/5/calls/3',
  parent: 'http://pbx.sipcentric.com/api/v1/customers/5',
  created: '2016-01-03T14:09:21Z',
  scope: 'DOMAIN',
  direction: 'IN',
  from: 'Josh Farrant <106>',
  to: '07777777777',
  callStarted: '2016-01-03T14:07:06Z',
  outcome: 'ANSWERED',
  duration: 122,
  cost: 0.183,
  callId: 'ast03-1452090857.6405603',
  linkedId: 'ast03-1452090840.6405562',
  links: {
    recordings: 'https://pbx.sipcentric.com/api/v1/customers/5/recordings?linkedId=ast02-1452095027.2374613'
  }
}];

module.exports = {

  singleObject: calls[2],

  listSingle: {
    totalItems: 1,
    pageSize: 20,
    page: 1,
    items: [calls[1]]
  },

  listMultiple: {
    totalItems: 3,
    pageSize: 20,
    page: 1,
    items: calls
  }
};
<|endoftext|>


<commit_before>/**
 * (c) 2016 Tieto Finland Oy
 * Licensed under the MIT license.
 */
'use strict';

/**
 * @ngdoc directive
 * @name dashboard.selectionList:selectionListDirective
 * @description
 * # selectionListDirective
 */
angular.module('dashboard')
    .directive('dbSelectionList', [function () {

        var controller = ['$log', '$scope', 'AttachmentData', 'CONST', '$rootScope', function ($log, $scope, AttachmentData, CONST, $rootScope) {
            $log.log("dbSelectionList: CONTROLLER");
            var self = this;
            self.data = null;
            self.isMobile = $rootScope.isMobile;

            self.selected = function (item) {
                $log.log("dbSelectionList: selected: " + JSON.stringify(item));
                $scope.onSelect({ data: item });
            };

            self.newTab = function (link) {
                if (angular.isString(link) && link.length) {
                    window.open(link, '_blank');
                }
                else {
                    $log.error("dbSelectionList: newTab: invalid link");
                }
            };

            self.isLinkDisabled = function (att) {
                var res = false;
                if (att instanceof AttachmentData) {
                    res = !angular.isString(att.link) || !att.link.length;
                } else {
                    $log.error("dbSelectionList: isLinkDisabled: unsupported arg type: " + JSON.stringify(att));
                }
                return res;
            };

            self.isOpenWindowDisabled = function (att) {
                var res = false;
                if (att instanceof AttachmentData) {
                    // disabled for secret docs because currently no easy way to popup them
                    res = (att.publicity === CONST.PUBLICITY.SECRET) || !angular.isString(att.link) || !att.link.length;
                } else {
                    $log.error("dbSelectionList: isOpenWindowDisabled: unsupported arg type: " + JSON.stringify(att));
                }
                return res;
            };

            $scope.$watch(function () {
                return {
                    selData: $scope.selData
                };
            }, function (data) {

                if (angular.isObject(data) && angular.isObject(data.selData)) {
                    self.data = data.selData;
                }
                else {
                    self.data = null;
                }
            }, true);

            $scope.$on('$destroy', function () {
                $log.debug("dbSelectionList: DESTROY");
            });
        }];

        return {
            scope: {
                selData: '=',
                onSelect: '&'
            },
            templateUrl: 'directives/selectionList/selectionList.Directive.html',
            restrict: 'AE',
            controller: controller,
            controllerAs: 'c',
            replace: 'true'
        };
    }]);
<commit_msg>selectionList to use a utils function for opening a file in new winddow<commit_after>/**
 * (c) 2016 Tieto Finland Oy
 * Licensed under the MIT license.
 */
'use strict';

/**
 * @ngdoc directive
 * @name dashboard.selectionList:selectionListDirective
 * @description
 * # selectionListDirective
 */
angular.module('dashboard')
    .directive('dbSelectionList', [function () {

        var controller = ['$log', '$scope', 'AttachmentData', 'CONST', '$rootScope', 'Utils', function ($log, $scope, AttachmentData, CONST, $rootScope, Utils) {
            $log.log("dbSelectionList: CONTROLLER");
            var self = this;
            self.data = null;
            self.isMobile = $rootScope.isMobile;

            self.selected = function (item) {
                $log.log("dbSelectionList: selected: " + JSON.stringify(item));
                $scope.onSelect({ data: item });
            };

            self.newTab = function (link) {
                Utils.openNewWin(link);
            };

            self.isLinkDisabled = function (att) {
                var res = false;
                if (att instanceof AttachmentData) {
                    res = !angular.isString(att.link) || !att.link.length;
                } else {
                    $log.error("dbSelectionList: isLinkDisabled: unsupported arg type: " + JSON.stringify(att));
                }
                return res;
            };

            self.isOpenWindowDisabled = function (att) {
                var res = false;
                if (att instanceof AttachmentData) {
                    // disabled for secret docs because currently no easy way to popup them
                    res = (att.publicity === CONST.PUBLICITY.SECRET) || !angular.isString(att.link) || !att.link.length;
                } else {
                    $log.error("dbSelectionList: isOpenWindowDisabled: unsupported arg type: " + JSON.stringify(att));
                }
                return res;
            };

            $scope.$watch(function () {
                return {
                    selData: $scope.selData
                };
            }, function (data) {

                if (angular.isObject(data) && angular.isObject(data.selData)) {
                    self.data = data.selData;
                }
                else {
                    self.data = null;
                }
            }, true);

            $scope.$on('$destroy', function () {
                $log.debug("dbSelectionList: DESTROY");
            });
        }];

        return {
            scope: {
                selData: '=',
                onSelect: '&'
            },
            templateUrl: 'directives/selectionList/selectionList.Directive.html',
            restrict: 'AE',
            controller: controller,
            controllerAs: 'c',
            replace: 'true'
        };
    }]);
<|endoftext|>


<commit_before>// Screen device
var Screen = function(canvas, cpu) {
    cpu.screen = this;
    this.cpu = cpu;

    this.colors = [
        0xFF,
        0xAA,
        0x55,
        0x00
    ];
    this.LCDC= 0xFF40;
    this.STAT= 0xFF41;
    this.SCY = 0xFF42;
    this.SCX = 0xFF43;
    this.LY  = 0xFF44;
    this.LYC = 0xFF45;
    this.WY  = 0xFF4A;
    this.WX  = 0xFF4B;

    this.vram = cpu.memory.vram.bind(cpu.memory);

    this.OAM_START = 0xFE00;
    this.OAM_END   = 0xFE9F;
    this.deviceram = cpu.memory.deviceram.bind(cpu.memory);
    this.oamram = cpu.memory.oamram.bind(cpu.memory);
    this.VBLANK_TIME = 70224;
    this.clock = 0;
    this.mode = 2;
    this.line = 0;

    canvas.width = Screen.physics.WIDTH * Screen.physics.PIXELSIZE;
    canvas.height = Screen.physics.HEIGHT * Screen.physics.PIXELSIZE;

    this.context = canvas.getContext('2d');
    this.imageData = this.context.createImageData(canvas.width, canvas.height);
};

Screen.physics = {
    WIDTH    : 160,
    HEIGHT   : 144,
    PIXELSIZE: 1,
    FREQUENCY: 60
};

Screen.tilemap = {
    HEIGHT: 32,
    WIDTH: 32,
    START_0: 0x9800,
    START_1: 0x9C00,
    LENGTH: 0x0400 // 1024 bytes = 32*32
};

Screen.prototype.update = function(clockElapsed) {
    this.clock += clockElapsed;
    var vblank = false;

    switch (this.mode) {
        case 0: // HBLANK
            if (this.clock >= 204) {
                this.clock -= 204;
                this.line++;
                this.updateLY();
                if (this.line == 144) {
                    this.setMode(1);
                    vblank = true;
                    this.cpu.requestInterrupt(Processor.INTERRUPTS.VBLANK);
                    this.drawFrame();
                } else {
                    this.setMode(2);
                }
            }
            break;
        case 1: // VBLANK
            if (this.clock >= 456) {
                this.clock -= 456;
                this.line++;
                if (this.line > 153) {
                    this.line = 0;
                    this.setMode(2);
                }
                this.updateLY();
            }

            break;
        case 2: // SCANLINE OAM
            if (this.clock >= 80) {
                this.clock -= 80;
                this.setMode(3);
            }
            break;
        case 3: // SCANLINE VRAM
            if (this.clock >= 172) {
                this.clock -= 172;
                this.setMode(0);
            }
            break;
    }

    return vblank;
};

Screen.prototype.updateLY = function() {
    this.deviceram(this.LY, this.line);
    var STAT = this.deviceram(this.STAT);
    if (this.deviceram(this.LY) == this.deviceram(this.LYC)) {
        this.deviceram(this.STAT, STAT | (1 << 2));
        if (STAT & (1 << 6)) {
            this.cpu.requestInterrupt(Processor.INTERRUPTS.LCDC);
        }
    } else {
        this.deviceram(this.STAT, STAT & (0xFF - (1 << 2)));
    }
};

Screen.prototype.setMode = function(mode) {
    this.mode = mode;
    var newSTAT = this.deviceram(this.STAT);
    newSTAT &= 0xFC;
    newSTAT |= mode;
    this.deviceram(this.STAT, newSTAT);

    if (mode < 3) {
        if (newSTAT & (1 << (3+mode))) {
            this.cpu.requestInterrupt(Processor.INTERRUPTS.LCDC);
        }
    }
};

Screen.prototype.drawFrame = function() {
    var LCDC = this.deviceram(this.LCDC);
    var enable = Memory.readBit(LCDC, 7);
    if (enable) {
        this.drawBackground(LCDC);
        this.drawSprites(LCDC);
        this.drawWindow(LCDC);
    }
    this.context.putImageData(this.imageData, 0, 0);
};

Screen.prototype.drawBackground = function(LCDC) {
    if (!Memory.readBit(LCDC, 0)) {
        return;
    }

    var buffer = new Array(256*256);
    var mapStart = Memory.readBit(LCDC, 3) ? Screen.tilemap.START_1 : Screen.tilemap.START_0;
    // browse BG tilemap
    for (var i = 0; i < Screen.tilemap.LENGTH; i++) {
        var tileIndex = this.vram(i + mapStart);

        var dataStart;
        if (Memory.readBit(LCDC, 4)) {
            dataStart = 0x8000;
        } else {
            dataStart = 0x8800;
            tileIndex = (tileIndex & 0x80 ? tileIndex-256 : tileIndex) + 128;
        }

        var tileData = this.readTileData(tileIndex, dataStart);
        var x = i % Screen.tilemap.WIDTH;
        var y = (i / Screen.tilemap.WIDTH) | 0;
        this.drawTile(tileData, x * 8, y * 8, buffer, 256);
    }

    var bgx = this.deviceram(this.SCX);
    var bgy = this.deviceram(this.SCY);
    for (var x = 0; x < Screen.physics.WIDTH; x++) {
        for (var y = 0; y < Screen.physics.HEIGHT; y++) {
            color = buffer[((x+bgx) & 255) + ((y+bgy) & 255) * 256];
            this.drawPixel(x, y, color);
        }
    }
};

Screen.prototype.drawSprites = function(LCDC) {
    if (!Memory.readBit(LCDC, 1)) {
        return;
    }
    var buffer = new Array(Screen.physics.WIDTH * Screen.physics.HEIGHT);
    for (var i = this.OAM_START; i < this.OAM_END; i += 4) {
        var y = this.oamram(i);
        var x = this.oamram(i+1);
        var tileIndex = this.oamram(i+2);
        var flags = this.oamram(i+3);

        if (y == 0 || y >= 160 || x == 0 || x >= 168) {
            continue;
        }
        var xflip = Memory.readBit(flags, 5);
        var yflip = Memory.readBit(flags, 6);
        var tileData = this.readTileData(tileIndex, 0x8000);
        this.drawTile(tileData, x - 8, y - 16, buffer, Screen.physics.WIDTH, xflip, yflip);
    }

    for (var x = 0; x < Screen.physics.WIDTH; x++) {
        for (var y = 0; y < Screen.physics.HEIGHT; y++) {
            color = buffer[x + y * 160];
            if (color === undefined || color === 0) continue;
            this.drawPixel(x, y, color);
        }
    }
};

Screen.prototype.drawTile = function(tileData, x, y, buffer, bufferWidth, xflip, yflip) {
    xflip = xflip || 0;
    yflip = yflip || 0;
    for (var line = 0; line < 8; line++) {
        var l = yflip ? 7 - line : line;
        var b1 = tileData.shift();
        var b2 = tileData.shift();

        for (var pixel = 0; pixel < 8; pixel++) {
            var mask = (1 << (7-pixel));
            var colorValue = ((b1 & mask) >> (7-pixel)) + ((b2 & mask) >> (7-pixel))*2;
            var p = xflip ? 7 - pixel : pixel;
            var bufferIndex = (x + p) + (y + l) * bufferWidth;
            buffer[bufferIndex] = colorValue;
        }
    }
};

Screen.prototype.readTileData = function(tileIndex, dataStart) {
    var tileSize  = 0x10; // 16 bytes / tile
    var tileData = new Array();

    var tileAddressStart = dataStart + (tileIndex * tileSize);
    for (var i = tileAddressStart; i < tileAddressStart + tileSize; i++) {
        tileData.push(this.vram(i));
    }

    return tileData;
};

Screen.prototype.drawWindow = function(LCDC) {
    if (!Memory.readBit(LCDC, 5)) {
        return;
    }

    var buffer = new Array(256*256);
    var mapStart = Memory.readBit(LCDC, 6) ? Screen.tilemap.START_1 : Screen.tilemap.START_0;
    // browse Window tilemap
    for (var i = 0; i < Screen.tilemap.LENGTH; i++) {
        var tileIndex = this.vram(i + mapStart);

        var dataStart;
        if (Memory.readBit(LCDC, 4)) {
            dataStart = 0x8000;
        } else {
            dataStart = 0x8800;
            tileIndex = (tileIndex & 0x80 ? tileIndex-256 : tileIndex) + 128;
        }

        var tileData = this.readTileData(tileIndex, dataStart);
        var x = i % Screen.tilemap.WIDTH;
        var y = (i / Screen.tilemap.WIDTH) | 0;
        this.drawTile(tileData, x * 8, y * 8, buffer, 256);
    }

    var wx = this.deviceram(this.WX) - 7;
    var wy = this.deviceram(this.WY);
    for (var x = Math.max(0, -wx); x < Math.min(Screen.physics.WIDTH, Screen.physics.WIDTH - wx); x++) {
        for (var y = Math.max(0, -wy); y < Math.min(Screen.physics.HEIGHT, Screen.physics.HEIGHT - wy); y++) {
            color = buffer[(x & 255) + (y & 255) * 256];
            this.drawPixel(x + wx, y + wy, color);
        }
    }
};

Screen.prototype.clearScreen = function() {
    this.context.fillStyle = '#FFF';
    this.context.fillRect(0, 0, Screen.physics.WIDTH * Screen.physics.PIXELSIZE, Screen.physics.HEIGHT * Screen.physics.PIXELSIZE);
};
Screen.prototype.drawPixel = function(x, y, color) {
    var v = this.colors[color];
    this.imageData.data[(y * 160 + x) * 4] = v;
    this.imageData.data[(y * 160 + x) * 4 + 1] = v;
    this.imageData.data[(y * 160 + x) * 4 + 2] = v;
    this.imageData.data[(y * 160 + x) * 4 + 3] = 255;
};
<commit_msg>improve performance for background rendering<commit_after>// Screen device
var Screen = function(canvas, cpu) {
    cpu.screen = this;
    this.cpu = cpu;

    this.colors = [
        0xFF,
        0xAA,
        0x55,
        0x00
    ];
    this.LCDC= 0xFF40;
    this.STAT= 0xFF41;
    this.SCY = 0xFF42;
    this.SCX = 0xFF43;
    this.LY  = 0xFF44;
    this.LYC = 0xFF45;
    this.WY  = 0xFF4A;
    this.WX  = 0xFF4B;

    this.vram = cpu.memory.vram.bind(cpu.memory);

    this.OAM_START = 0xFE00;
    this.OAM_END   = 0xFE9F;
    this.deviceram = cpu.memory.deviceram.bind(cpu.memory);
    this.oamram = cpu.memory.oamram.bind(cpu.memory);
    this.VBLANK_TIME = 70224;
    this.clock = 0;
    this.mode = 2;
    this.line = 0;

    canvas.width = Screen.physics.WIDTH * Screen.physics.PIXELSIZE;
    canvas.height = Screen.physics.HEIGHT * Screen.physics.PIXELSIZE;

    this.context = canvas.getContext('2d');
    this.imageData = this.context.createImageData(canvas.width, canvas.height);
};

Screen.physics = {
    WIDTH    : 160,
    HEIGHT   : 144,
    PIXELSIZE: 1,
    FREQUENCY: 60
};

Screen.tilemap = {
    HEIGHT: 32,
    WIDTH: 32,
    START_0: 0x9800,
    START_1: 0x9C00,
    LENGTH: 0x0400 // 1024 bytes = 32*32
};

Screen.prototype.update = function(clockElapsed) {
    this.clock += clockElapsed;
    var vblank = false;

    switch (this.mode) {
        case 0: // HBLANK
            if (this.clock >= 204) {
                this.clock -= 204;
                this.line++;
                this.updateLY();
                if (this.line == 144) {
                    this.setMode(1);
                    vblank = true;
                    this.cpu.requestInterrupt(Processor.INTERRUPTS.VBLANK);
                    this.drawFrame();
                } else {
                    this.setMode(2);
                }
            }
            break;
        case 1: // VBLANK
            if (this.clock >= 456) {
                this.clock -= 456;
                this.line++;
                if (this.line > 153) {
                    this.line = 0;
                    this.setMode(2);
                }
                this.updateLY();
            }

            break;
        case 2: // SCANLINE OAM
            if (this.clock >= 80) {
                this.clock -= 80;
                this.setMode(3);
            }
            break;
        case 3: // SCANLINE VRAM
            if (this.clock >= 172) {
                this.clock -= 172;
                this.setMode(0);
            }
            break;
    }

    return vblank;
};

Screen.prototype.updateLY = function() {
    this.deviceram(this.LY, this.line);
    var STAT = this.deviceram(this.STAT);
    if (this.deviceram(this.LY) == this.deviceram(this.LYC)) {
        this.deviceram(this.STAT, STAT | (1 << 2));
        if (STAT & (1 << 6)) {
            this.cpu.requestInterrupt(Processor.INTERRUPTS.LCDC);
        }
    } else {
        this.deviceram(this.STAT, STAT & (0xFF - (1 << 2)));
    }
};

Screen.prototype.setMode = function(mode) {
    this.mode = mode;
    var newSTAT = this.deviceram(this.STAT);
    newSTAT &= 0xFC;
    newSTAT |= mode;
    this.deviceram(this.STAT, newSTAT);

    if (mode < 3) {
        if (newSTAT & (1 << (3+mode))) {
            this.cpu.requestInterrupt(Processor.INTERRUPTS.LCDC);
        }
    }
};

Screen.prototype.drawFrame = function() {
    var LCDC = this.deviceram(this.LCDC);
    var enable = Memory.readBit(LCDC, 7);
    if (enable) {
        this.drawBackground(LCDC);
        this.drawSprites(LCDC);
        this.drawWindow(LCDC);
    }
    this.context.putImageData(this.imageData, 0, 0);
};

Screen.prototype.drawBackground = function(LCDC) {
    if (!Memory.readBit(LCDC, 0)) {
        return;
    }

    var buffer = new Array(256*256);
    var mapStart = Memory.readBit(LCDC, 3) ? Screen.tilemap.START_1 : Screen.tilemap.START_0;

    var dataStart, signedIndex = false;
    if (Memory.readBit(LCDC, 4)) {
        dataStart = 0x8000;
    } else {
        dataStart = 0x8800;
        signedIndex = true;
    }

    // browse BG tilemap
    for (var i = 0; i < Screen.tilemap.LENGTH; i++) {
        var tileIndex = this.vram(i + mapStart);

        if (signedIndex) {
            tileIndex = ops._getSignedValue(tileIndex) + 128;
        }

        var tileData = this.readTileData(tileIndex, dataStart);
        var x = i % Screen.tilemap.WIDTH;
        var y = (i / Screen.tilemap.WIDTH) | 0;
        this.drawTile(tileData, x * 8, y * 8, buffer, 256);
    }

    var bgx = this.deviceram(this.SCX);
    var bgy = this.deviceram(this.SCY);
    for (var x = 0; x < Screen.physics.WIDTH; x++) {
        for (var y = 0; y < Screen.physics.HEIGHT; y++) {
            color = buffer[((x+bgx) & 255) + ((y+bgy) & 255) * 256];
            this.drawPixel(x, y, color);
        }
    }
};

Screen.prototype.drawSprites = function(LCDC) {
    if (!Memory.readBit(LCDC, 1)) {
        return;
    }
    var buffer = new Array(Screen.physics.WIDTH * Screen.physics.HEIGHT);
    for (var i = this.OAM_START; i < this.OAM_END; i += 4) {
        var y = this.oamram(i);
        var x = this.oamram(i+1);
        var tileIndex = this.oamram(i+2);
        var flags = this.oamram(i+3);

        if (y == 0 || y >= 160 || x == 0 || x >= 168) {
            continue;
        }
        var xflip = Memory.readBit(flags, 5);
        var yflip = Memory.readBit(flags, 6);
        var tileData = this.readTileData(tileIndex, 0x8000);
        this.drawTile(tileData, x - 8, y - 16, buffer, Screen.physics.WIDTH, xflip, yflip);
    }

    for (var x = 0; x < Screen.physics.WIDTH; x++) {
        for (var y = 0; y < Screen.physics.HEIGHT; y++) {
            color = buffer[x + y * 160];
            if (color === undefined || color === 0) continue;
            this.drawPixel(x, y, color);
        }
    }
};

Screen.prototype.drawTile = function(tileData, x, y, buffer, bufferWidth, xflip, yflip) {
    xflip = xflip || 0;
    yflip = yflip || 0;
    for (var line = 0; line < 8; line++) {
        var l = yflip ? 7 - line : line;
        var b1 = tileData.shift();
        var b2 = tileData.shift();

        for (var pixel = 0; pixel < 8; pixel++) {
            var mask = (1 << (7-pixel));
            var colorValue = ((b1 & mask) >> (7-pixel)) + ((b2 & mask) >> (7-pixel))*2;
            var p = xflip ? 7 - pixel : pixel;
            var bufferIndex = (x + p) + (y + l) * bufferWidth;
            buffer[bufferIndex] = colorValue;
        }
    }
};

Screen.prototype.readTileData = function(tileIndex, dataStart) {
    var tileSize  = 0x10; // 16 bytes / tile
    var tileData = new Array();

    var tileAddressStart = dataStart + (tileIndex * tileSize);
    for (var i = tileAddressStart; i < tileAddressStart + tileSize; i++) {
        tileData.push(this.vram(i));
    }

    return tileData;
};

Screen.prototype.drawWindow = function(LCDC) {
    if (!Memory.readBit(LCDC, 5)) {
        return;
    }

    var buffer = new Array(256*256);
    var mapStart = Memory.readBit(LCDC, 6) ? Screen.tilemap.START_1 : Screen.tilemap.START_0;

    var dataStart, signedIndex = false;
    if (Memory.readBit(LCDC, 4)) {
        dataStart = 0x8000;
    } else {
        dataStart = 0x8800;
        signedIndex = true;
    }

    // browse Window tilemap
    for (var i = 0; i < Screen.tilemap.LENGTH; i++) {
        var tileIndex = this.vram(i + mapStart);

        if (signedIndex) {
            tileIndex = ops._getSignedValue(tileIndex) + 128;
        }

        var tileData = this.readTileData(tileIndex, dataStart);
        var x = i % Screen.tilemap.WIDTH;
        var y = (i / Screen.tilemap.WIDTH) | 0;
        this.drawTile(tileData, x * 8, y * 8, buffer, 256);
    }

    var wx = this.deviceram(this.WX) - 7;
    var wy = this.deviceram(this.WY);
    for (var x = Math.max(0, -wx); x < Math.min(Screen.physics.WIDTH, Screen.physics.WIDTH - wx); x++) {
        for (var y = Math.max(0, -wy); y < Math.min(Screen.physics.HEIGHT, Screen.physics.HEIGHT - wy); y++) {
            color = buffer[(x & 255) + (y & 255) * 256];
            this.drawPixel(x + wx, y + wy, color);
        }
    }
};

Screen.prototype.clearScreen = function() {
    this.context.fillStyle = '#FFF';
    this.context.fillRect(0, 0, Screen.physics.WIDTH * Screen.physics.PIXELSIZE, Screen.physics.HEIGHT * Screen.physics.PIXELSIZE);
};
Screen.prototype.drawPixel = function(x, y, color) {
    var v = this.colors[color];
    this.imageData.data[(y * 160 + x) * 4] = v;
    this.imageData.data[(y * 160 + x) * 4 + 1] = v;
    this.imageData.data[(y * 160 + x) * 4 + 2] = v;
    this.imageData.data[(y * 160 + x) * 4 + 3] = 255;
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import EmptyObject from 'documents/util/empty-object';

const {
  computed,
  copy,
  merge
} = Ember;

// usage:
//
// return destroyable('database', {
//   create(key) {
//     return new Thing(key);
//   },
//   get(internal) {
//     return internal.model(true);
//   },
//   destroy(internal) {
//     internal.destroy();
//   }
// });

const __ember_cli_documents__ = '__ember_cli_documents__';

const _get = owner => {
  return owner.willDestroy[__ember_cli_documents__];
}

const _cache = (owner, create) => {
  if(!owner) {
    return;
  }
  if(!owner.willDestroy) {
    return;
  }
  let object = _get(owner);
  if(!object && create) {
    object = new EmptyObject();
    let willDestroy = owner.willDestroy;
    owner.willDestroy = function() {
      let object = _get(owner);
      for(let key in object) {
        let { value, destroy } = object[key];
        destroy(value);
      }
      willDestroy.apply(owner, arguments);
    }
    owner.willDestroy[__ember_cli_documents__] = object;
  }
  return object;
}

const _cacheFor = (owner, key) => {
  let cache = _cache(owner, false);
  if(!cache) {
    return {};
  }
  return cache[key];
}

const _destroyCached = (owner, key, value, destroy) => {
  let cache = _cache(owner);
  delete cache[key];
  destroy(value);
}

const _store = (owner, key, value, destroy) => {
  let cache = _cache(owner, true);
  cache[key] = { value, destroy };
}

export const cacheFor = (owner, key) => {
  let cache = _cache(owner);
  if(!cache) {
    return;
  }
  let hash = cache[key];
  if(!hash) {
    return;
  }
  return hash.value;
}

const defaults = {
  get(internal) {
    return internal.model(true);
  },
  destroy(internal) {
    internal.destroy();
  }
};

export default (...args) => {
  let opts = args.pop();
  opts = merge(copy(defaults), opts);
  return computed(...args, function(key) {
    let { value, destroy } = _cacheFor(this, key);

    if(value) {
      _destroyCached(this, key, value, destroy);
    }

    value = opts.create.call(this, key);

    if(!value) {
      return;
    }

    _store(this, key, value, opts.destroy);

    if(opts.get) {
      return opts.get.call(this, value, key);
    }

    return value;
  });
}
<commit_msg>fix destroyable cache lookup<commit_after>import Ember from 'ember';
import EmptyObject from 'documents/util/empty-object';

const {
  computed,
  copy,
  merge
} = Ember;

// usage:
//
// return destroyable('database', {
//   create(key) {
//     return new Thing(key);
//   },
//   get(internal) {
//     return internal.model(true);
//   },
//   destroy(internal) {
//     internal.destroy();
//   }
// });

const __ember_cli_documents__ = '__ember_cli_documents__';

const _get = owner => {
  return owner.willDestroy[__ember_cli_documents__];
}

const _cache = (owner, create) => {
  if(!owner) {
    return;
  }
  if(!owner.willDestroy) {
    return;
  }
  let object = _get(owner);
  if(!object && create) {
    object = new EmptyObject();
    let willDestroy = owner.willDestroy;
    owner.willDestroy = function() {
      let object = _get(owner);
      for(let key in object) {
        let { value, destroy } = object[key];
        destroy(value);
      }
      willDestroy.apply(owner, arguments);
    }
    owner.willDestroy[__ember_cli_documents__] = object;
  }
  return object;
}

const _cacheFor = (owner, key) => {
  let cache = _cache(owner, false);
  if(!cache) {
    return {};
  }
  let value = cache[key];
  if(!value) {
    value = {};
  }
  return value;
}

const _destroyCached = (owner, key, value, destroy) => {
  let cache = _cache(owner);
  delete cache[key];
  destroy(value);
}

const _store = (owner, key, value, destroy) => {
  let cache = _cache(owner, true);
  cache[key] = { value, destroy };
}

export const cacheFor = (owner, key) => {
  let cache = _cache(owner);
  if(!cache) {
    return;
  }
  let hash = cache[key];
  if(!hash) {
    return;
  }
  return hash.value;
}

const defaults = {
  get(internal) {
    return internal.model(true);
  },
  destroy(internal) {
    internal.destroy();
  }
};

export default (...args) => {
  let opts = args.pop();
  opts = merge(copy(defaults), opts);
  return computed(...args, function(key) {
    let { value, destroy } = _cacheFor(this, key);

    if(value) {
      _destroyCached(this, key, value, destroy);
    }

    value = opts.create.call(this, key);

    if(!value) {
      return;
    }

    _store(this, key, value, opts.destroy);

    if(opts.get) {
      return opts.get.call(this, value, key);
    }

    return value;
  });
}
<|endoftext|>