<commit_before>define(['util', 'fs', 'endianbuffer', 'x_types', 'x_client', 'keymap'], function (util, fs, EndianBuffer, x_types, XServerClient, keymap) {
  var module = { exports: {} };

  var default_atoms = [
      'PRIMARY'
    , 'SECONDARY'
    , 'ARC'
    , 'ATOM'
    , 'BITMAP'
    , 'CARDINAL'
    , 'COLORMAP'
    , 'CURSOR'
    , 'CUT_BUFFER0'
    , 'CUT_BUFFER1'
    , 'CUT_BUFFER2'
    , 'CUT_BUFFER3'
    , 'CUT_BUFFER4'
    , 'CUT_BUFFER5'
    , 'CUT_BUFFER6'
    , 'CUT_BUFFER7'
    , 'DRAWABLE'
    , 'FONT'
    , 'INTEGER'
    , 'PIXMAP'
    , 'POINT'
    , 'RECTANGLE'
    , 'RESOURCE_MANAGER'
    , 'RGB_COLOR_MAP'
    , 'RGB_BEST_MAP'
    , 'RGB_BLUE_MAP'
    , 'RGB_DEFAULT_MAP'
    , 'RGB_GRAY_MAP'
    , 'RGB_GREEN_MAP'
    , 'RGB_RED_MAP'
    , 'STRING'
    , 'VISUALID'
    , 'WINDOW'
    , 'WM_COMMAND'
    , 'WM_HINTS'
    , 'WM_CLIENT_MACHINE'
    , 'WM_ICON_NAME'
    , 'WM_ICON_SIZE'
    , 'WM_NAME'
    , 'WM_NORMAL_HINTS'
    , 'WM_SIZE_HINTS'
    , 'WM_ZOOM_HINTS'
    , 'MIN_SPACE'
    , 'NORM_SPACE'
    , 'MAX_SPACE'
    , 'END_SPACE'
    , 'SUPERSCRIPT_X'
    , 'SUPERSCRIPT_Y'
    , 'SUBSCRIPT_X'
    , 'SUBSCRIPT_Y'
    , 'UNDERLINE_POSITION'
    , 'UNDERLINE_THICKNESS'
    , 'STRIKEOUT_ASCENT'
    , 'STRIKEOUT_DESCENT'
    , 'ITALIC_ANGLE'
    , 'X_HEIGHT'
    , 'QUAD_WIDTH'
    , 'WEIGHT'
    , 'POINT_SIZE'
    , 'RESOLUTION'
    , 'COPYRIGHT'
    , 'NOTICE'
    , 'FONT_NAME'
    , 'FAMILY_NAME'
    , 'FULL_NAME'
    , 'CAP_HEIGHT'
    , 'WM_CLASS'
    , 'WM_TRANSIENT_FOR'
  ]

  function XServer (id, socket, screen) {
    Object.defineProperty(this, 'server', {
        enumerable: false
      , value: this
    });
    this.id = id;
    this.socket = socket;
    this.screen = screen;
    this.protocol_major = 11;
    this.protocol_minor = 0;
    this.release = 11300000;
    this.vendor = 'JavaScript X';
    this.event_cache = [];
    this.grab = null;
    this.grab_buffer = [];
    this.grab_pointer = null;
    this.grab_keyboard = null;
    this.buttons = 0;
    this.input_focus = null;
    this.input_focus_revert = 0;
    this.keymap = keymap.maps.gb.clone();
    this.resource_id_mask = 0x001fffff;
    this.resource_id_bases = [];
    var res_base = this.resource_id_mask + 1;
    var res_id = 0;
    while (!(res_id & 0xE0000000)) {
      this.resource_id_bases.push(res_id += res_base);
    }

    this.formats = [
        new x_types.Format(0x01, 0x01, 0x20)
      , new x_types.Format(0x04, 0x08, 0x20)
      , new x_types.Format(0x08, 0x08, 0x20)
      , new x_types.Format(0x0f, 0x10, 0x20)
      , new x_types.Format(0x10, 0x10, 0x20)
      , new x_types.Format(0x18, 0x20, 0x20)
      , new x_types.Format(0x20, 0x20, 0x20)
    ];
    this.screens = [
        new x_types.Screen(
            0x00000026 // root
          , 0x00000022 // def colormap
          , 0x00ffffff // white
          , 0x00000000 // black
          , 0x00000000 // current input masks
          , $('.screen').width() // width px
          , $('.screen').height() // height px
          , Math.round($('.screen').width() / (96 / 25.4)) // width mm
          , Math.round($('.screen').height() / (96 / 25.4)) // height mm
          , 0x0001 // min maps
          , 0x0001 // max maps
          , 0x20 // root visual
          , 2 // backing stores 0 Never, 1 WhenMapped, 2 Always
          , 1 // save unders
          , 0x18 //24 default depth
          , [ // depths
                new x_types.Depth(
                    0x01 // depth 1
                )
              , new x_types.Depth(
                    0x18 // depth 24
                  , [ // visualtypes
                        new x_types.VisualType(
                            0x00000020 // id
                          , 0x04 // class
                          , 0x08 // bits per rgb
                          , 0x0100 // colormap entries
                          , 0x00ff0000 // red mask
                          , 0x0000ff00 // green mask
                          , 0x000000ff // blue mask
                        )
                      , new x_types.VisualType(
                            0x00000021 // id
                          , 0x05 // class
                          , 0x08 // bits per rgb
                          , 0x0100 // colormap entries
                          , 0x00ff0000 // red mask
                          , 0x0000ff00 // green mask
                          , 0x000000ff // blue mask
                        )
                    ]
                )
            ]
        )
    ];
    this.atoms = default_atoms.slice();
    this.atom_owners = [];
    this.resources = {}
    this.resources[0x00000022] = new x_types.ColorMap(
        0x00000022
      , function (rgb, type) {
          rgb = [
              (rgb & 0x000000ff)
            , (rgb & 0x0000ff00) >> 8
            , (rgb & 0x00ff0000) >> 16
          ];
          switch (type) {
            case 'hex':
              return rgb.reduce(function (str, v) { v = v.toString(16); return str + (v.length < 2 ? '0' : '') + v }, '');
            default:
              return rgb;
          }
        }
    );
    this.resources[0x00000026] = this.root = new x_types.Window(
        this
      , 0x00000026
      , 0x18 // depth 24
      , 0, 0
      , this.screen.width(), this.screen.height()
      , 0, 1, 0, 0, 0
    );
    this.root.parent = { element: $('.screen'), owner: this, children: [this.root] }
    this.font_path = 'fonts';
    this.fonts_dir = {};
    this.fonts_scale = {};
    this.fonts_cache = {};

    fs.readFile(this.font_path + '/fonts.dir', 'utf8', function (err, file) {
      if (err)
        throw new Error('No fonts.dir');
      file = file.split('\n');
      file.pop();
      var count = file.shift() ^ 0;
      if (count !== file.length)
        throw new Error('Invalid length of fonts.dir');
      file.forEach(function (line) {
        var match = line.match(/^(".*"|[^ ]*) (.*)$/);
        this.fonts_dir[match[2]] = match [1];
      }.bind(this));
    }.bind(this));

    this.clients = {};
    var c = this.resources[0x00000026].canvas[0].getContext('2d')
      , img = new Image;
    img.onload = function () {
      c.rect(0, 0, $('.screen').width(), $('.screen').height());
      c.fillStyle = c.createPattern(img, 'repeat');
      c.fill();
    }
    img.src = "/check.png";

    this.resources[0x00000026].map();

    this.mouseX = this.mouseY = 0;
    var self = this;
    this.screen.on('mousemove.xy', function (event) {
      self.mouseX = event.offsetX;
      self.mouseY = event.offsetY;
    });
  }

  XServer.prototype.getFormatByDepth = function (depth) {
    return this.formats.filter(function (format) {
      return format.depth === depth;
    })[0];
  }

  XServer.prototype.newClient = function (id) {
    return this.clients[id] = new XServerClient(this, id, this.resource_id_bases.shift(), this.resource_id_mask);
  }

  XServer.prototype.disconnect = function (id) {
    if (!id) {
      this.screen.off('');
      return Object.keys(this.clients).forEach(function (cid) {
        this.clients[cid].disconnect();
      }.bind(this));
    } // Disconnect whole server
    if (!this.clients[id])
      throw new Error('Invalid client! Disconnected?');

    if (this.grab && this.grab.id !== id) {
      return this.grab_buffer.push([this, 'disconnect', id]);
    }

    this.resource_id_bases.push(this.clients[id].resource_id_base);
    this.clients[id].disconnect();
  }

  XServer.prototype.sendEvent = function () {}

  XServer.prototype.write = function (client, data) {
    if (! this.clients[client.id])
      throw new Error('Invalid client! Disconnected?');
    if (! (data instanceof EndianBuffer))
      throw new Error('Not a buffer!');
    var buffer = new EndianBuffer(data.length + 2);
    buffer.endian = true; // Client id is always Little Endian
    data.copy(buffer, 2);
    buffer.writeUInt16(client.id, 0);
    this.socket.send(buffer.buffer);
  }

  XServer.prototype.processData = function (buffer) {
    var id = buffer.readUInt16(0)
      , data = buffer.slice(2);

    if (! this.clients[id])
      throw new Error('Invalid client! Disconnected?');
    this.clients[id].processData(data);
  }

  XServer.prototype.getResource = function (id, Type, allowed_values) {
    var resource = this.resources[id];
    allowed_values = Array.isArray(allowed_values) ?
      allowed_values : Array.prototype.slice.call(arguments, 2);
    if (~ allowed_values.indexOf(id))
      return id;
    if (Type && !(resource instanceof Type))
      throw new x_types.Error({}, Type.error_code || 1, id);
    return resource;
  }

  XServer.prototype.putResource = function (resource) {
    var owner = resource.owner;
    console.warn('server.putResource', resource.id, resource);
    if (((~ owner.resource_id_mask) & owner.resource_id_base) !== owner.resource_id_base)
      throw new x_types.Error({}, 14 /* IDChoise */, resource.id);
    if (this.resources[resource.id])
      throw new x_types.Error({}, 14 /* IDChoice */, resource.id);
    return this.resources[resource.id] = resource;
  }

  XServer.prototype.freeResource = function (id, Type) {
    var resource = this.resources[id];
    console.warn('server.freeResource', id, resource);
    if (Type && !(resource instanceof Type))
      throw new x_types.Error({}, Type.error_code || 1, id);
    delete this.resources[id];
  }

  XServer.prototype.getAtom = function (id) {
    var atom = this.atoms[id];
    if (! atom)
      throw new x_types.Error({}, 5, id);
    return atom;
  }

  XServer.prototype.flushGrabBuffer = function() {
    var item = null;
    while (item = this.grab_buffer.shift())
      item[0][item[1]].apply(item[0], item.slice(2));
    Object.keys(this.clients).forEach(function (id) {
      this.clients[id].reqs_processing = false;
      this.clients[id].processReqs();
    }.bind(this));
  }

  XServer.prototype.listFonts = function (pattern) {
    var re = new RegExp('^' + pattern.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1").replace(/\\([*?])/g, '.$1') + '$', 'i');
    return Object.keys(this.fonts_dir).filter(function(name) { return re.test(name) });
  }

  XServer.prototype.resolveFont = function (name) {
    var resolved_name;
    if (! (name in this.fonts_dir)) {
      if (/[*?]/.test(name)) {
        var names = this.listFonts(name);
        if (names && names.length)
          resolved_name = [names[0], this.fonts_dir[names[0]]];
      }
    }
    return resolved_name || [name, this.fonts_dir[name]];
  }

  XServer.prototype.loadFont = function (resolved_name, server_name, callback) {
    var self = this;
    console.log('XServer.loadFont', [].slice.call(arguments));
    console.log('Font cached?', resolved_name in this.fonts_cache);
    if (resolved_name in this.fonts_cache)
      return callback(null, this.fonts_cache[resolved_name]);
    self.grabbed = 'loadFont';
    fs.readFile('fonts/' + resolved_name + '.meta.json', 'utf8', function (err, meta) {
      console.log('read meta');
      if (err)
        return callback(err);
      try {
        meta = JSON.parse(meta);
      } catch (e) {
        return callback(e);
      }
      var font = new x_types.Font(meta.type, resolved_name, server_name);
      font.meta = meta;
      font.loadData(function (err) {
        self.grabbed = null;
        console.log('XServer.loadFont callback', [].slice.call(arguments));
        if (!err) {
          console.log('Font loaded', font);
        }
        self.fonts_cache[resolved_name] = font;
        console.log('Font loaded2', font);
        callback(err, font);
      });
    });
  }

  XServer.prototype.openFont = function (client, fid, name) {
    var self = this
      , resolved_name = this.resolveFont(name);
    if (resolved_name[1]) {
      this.grab = 'openFont';
      this.loadFont(resolved_name[1], resolved_name[0], function (err, font) {
        font.id = fid;
        font.owner = client;
        if (err)
          font.error = err;
        else
          self.putResource(font);
        console.log('XServer.openFont callback', err, font);
        self.grab = false;
        self.flushGrabBuffer();
      });
      return;
    }
    console.log('Name not resolved', name);
    this.grab = false;
    this.flushGrabBuffer();

  }

  XServer.prototype.encodeString = function (str) {
    var out_str = '';
    if (typeof str !== 'string')
      return str;
    for (var i = 0; i < str.length; i ++) {
      var v = str.charCodeAt(i);
      if (v < 0x21) {
        out_str += String.fromCharCode(0x2400 + v);
        continue;
      }
      if (v > 0x7e && v < 0xa1) {
        out_str += String.fromCharCode(0xf800 - 0x7e + v);
        continue;
      }
      out_str += str.charAt(i);
    }
    return out_str;
  }

  return XServer;
});
<commit_msg>x_server.getAtom now checks object instance<commit_after>define(['util', 'fs', 'endianbuffer', 'x_types', 'x_client', 'keymap'], function (util, fs, EndianBuffer, x_types, XServerClient, keymap) {
  var module = { exports: {} };

  var default_atoms = [
      'PRIMARY'
    , 'SECONDARY'
    , 'ARC'
    , 'ATOM'
    , 'BITMAP'
    , 'CARDINAL'
    , 'COLORMAP'
    , 'CURSOR'
    , 'CUT_BUFFER0'
    , 'CUT_BUFFER1'
    , 'CUT_BUFFER2'
    , 'CUT_BUFFER3'
    , 'CUT_BUFFER4'
    , 'CUT_BUFFER5'
    , 'CUT_BUFFER6'
    , 'CUT_BUFFER7'
    , 'DRAWABLE'
    , 'FONT'
    , 'INTEGER'
    , 'PIXMAP'
    , 'POINT'
    , 'RECTANGLE'
    , 'RESOURCE_MANAGER'
    , 'RGB_COLOR_MAP'
    , 'RGB_BEST_MAP'
    , 'RGB_BLUE_MAP'
    , 'RGB_DEFAULT_MAP'
    , 'RGB_GRAY_MAP'
    , 'RGB_GREEN_MAP'
    , 'RGB_RED_MAP'
    , 'STRING'
    , 'VISUALID'
    , 'WINDOW'
    , 'WM_COMMAND'
    , 'WM_HINTS'
    , 'WM_CLIENT_MACHINE'
    , 'WM_ICON_NAME'
    , 'WM_ICON_SIZE'
    , 'WM_NAME'
    , 'WM_NORMAL_HINTS'
    , 'WM_SIZE_HINTS'
    , 'WM_ZOOM_HINTS'
    , 'MIN_SPACE'
    , 'NORM_SPACE'
    , 'MAX_SPACE'
    , 'END_SPACE'
    , 'SUPERSCRIPT_X'
    , 'SUPERSCRIPT_Y'
    , 'SUBSCRIPT_X'
    , 'SUBSCRIPT_Y'
    , 'UNDERLINE_POSITION'
    , 'UNDERLINE_THICKNESS'
    , 'STRIKEOUT_ASCENT'
    , 'STRIKEOUT_DESCENT'
    , 'ITALIC_ANGLE'
    , 'X_HEIGHT'
    , 'QUAD_WIDTH'
    , 'WEIGHT'
    , 'POINT_SIZE'
    , 'RESOLUTION'
    , 'COPYRIGHT'
    , 'NOTICE'
    , 'FONT_NAME'
    , 'FAMILY_NAME'
    , 'FULL_NAME'
    , 'CAP_HEIGHT'
    , 'WM_CLASS'
    , 'WM_TRANSIENT_FOR'
  ]

  function XServer (id, socket, screen) {
    Object.defineProperty(this, 'server', {
        enumerable: false
      , value: this
    });
    this.id = id;
    this.socket = socket;
    this.screen = screen;
    this.protocol_major = 11;
    this.protocol_minor = 0;
    this.release = 11300000;
    this.vendor = 'JavaScript X';
    this.event_cache = [];
    this.grab = null;
    this.grab_buffer = [];
    this.grab_pointer = null;
    this.grab_keyboard = null;
    this.buttons = 0;
    this.input_focus = null;
    this.input_focus_revert = 0;
    this.keymap = keymap.maps.gb.clone();
    this.resource_id_mask = 0x001fffff;
    this.resource_id_bases = [];
    var res_base = this.resource_id_mask + 1;
    var res_id = 0;
    while (!(res_id & 0xE0000000)) {
      this.resource_id_bases.push(res_id += res_base);
    }

    this.formats = [
        new x_types.Format(0x01, 0x01, 0x20)
      , new x_types.Format(0x04, 0x08, 0x20)
      , new x_types.Format(0x08, 0x08, 0x20)
      , new x_types.Format(0x0f, 0x10, 0x20)
      , new x_types.Format(0x10, 0x10, 0x20)
      , new x_types.Format(0x18, 0x20, 0x20)
      , new x_types.Format(0x20, 0x20, 0x20)
    ];
    this.screens = [
        new x_types.Screen(
            0x00000026 // root
          , 0x00000022 // def colormap
          , 0x00ffffff // white
          , 0x00000000 // black
          , 0x00000000 // current input masks
          , $('.screen').width() // width px
          , $('.screen').height() // height px
          , Math.round($('.screen').width() / (96 / 25.4)) // width mm
          , Math.round($('.screen').height() / (96 / 25.4)) // height mm
          , 0x0001 // min maps
          , 0x0001 // max maps
          , 0x20 // root visual
          , 2 // backing stores 0 Never, 1 WhenMapped, 2 Always
          , 1 // save unders
          , 0x18 //24 default depth
          , [ // depths
                new x_types.Depth(
                    0x01 // depth 1
                )
              , new x_types.Depth(
                    0x18 // depth 24
                  , [ // visualtypes
                        new x_types.VisualType(
                            0x00000020 // id
                          , 0x04 // class
                          , 0x08 // bits per rgb
                          , 0x0100 // colormap entries
                          , 0x00ff0000 // red mask
                          , 0x0000ff00 // green mask
                          , 0x000000ff // blue mask
                        )
                      , new x_types.VisualType(
                            0x00000021 // id
                          , 0x05 // class
                          , 0x08 // bits per rgb
                          , 0x0100 // colormap entries
                          , 0x00ff0000 // red mask
                          , 0x0000ff00 // green mask
                          , 0x000000ff // blue mask
                        )
                    ]
                )
            ]
        )
    ];
    this.atoms = default_atoms.slice();
    this.atom_owners = [];
    this.resources = {}
    this.resources[0x00000022] = new x_types.ColorMap(
        0x00000022
      , function (rgb, type) {
          rgb = [
              (rgb & 0x000000ff)
            , (rgb & 0x0000ff00) >> 8
            , (rgb & 0x00ff0000) >> 16
          ];
          switch (type) {
            case 'hex':
              return rgb.reduce(function (str, v) { v = v.toString(16); return str + (v.length < 2 ? '0' : '') + v }, '');
            default:
              return rgb;
          }
        }
    );
    this.resources[0x00000026] = this.root = new x_types.Window(
        this
      , 0x00000026
      , 0x18 // depth 24
      , 0, 0
      , this.screen.width(), this.screen.height()
      , 0, 1, 0, 0, 0
    );
    this.root.parent = { element: $('.screen'), owner: this, children: [this.root] }
    this.font_path = 'fonts';
    this.fonts_dir = {};
    this.fonts_scale = {};
    this.fonts_cache = {};

    fs.readFile(this.font_path + '/fonts.dir', 'utf8', function (err, file) {
      if (err)
        throw new Error('No fonts.dir');
      file = file.split('\n');
      file.pop();
      var count = file.shift() ^ 0;
      if (count !== file.length)
        throw new Error('Invalid length of fonts.dir');
      file.forEach(function (line) {
        var match = line.match(/^(".*"|[^ ]*) (.*)$/);
        this.fonts_dir[match[2]] = match [1];
      }.bind(this));
    }.bind(this));

    this.clients = {};
    var c = this.resources[0x00000026].canvas[0].getContext('2d')
      , img = new Image;
    img.onload = function () {
      c.rect(0, 0, $('.screen').width(), $('.screen').height());
      c.fillStyle = c.createPattern(img, 'repeat');
      c.fill();
    }
    img.src = "/check.png";

    this.resources[0x00000026].map();

    this.mouseX = this.mouseY = 0;
    var self = this;
    this.screen.on('mousemove.xy', function (event) {
      self.mouseX = event.offsetX;
      self.mouseY = event.offsetY;
    });
  }

  XServer.prototype.getFormatByDepth = function (depth) {
    return this.formats.filter(function (format) {
      return format.depth === depth;
    })[0];
  }

  XServer.prototype.newClient = function (id) {
    return this.clients[id] = new XServerClient(this, id, this.resource_id_bases.shift(), this.resource_id_mask);
  }

  XServer.prototype.disconnect = function (id) {
    if (!id) {
      this.screen.off('');
      return Object.keys(this.clients).forEach(function (cid) {
        this.clients[cid].disconnect();
      }.bind(this));
    } // Disconnect whole server
    if (!this.clients[id])
      throw new Error('Invalid client! Disconnected?');

    if (this.grab && this.grab.id !== id) {
      return this.grab_buffer.push([this, 'disconnect', id]);
    }

    this.resource_id_bases.push(this.clients[id].resource_id_base);
    this.clients[id].disconnect();
  }

  XServer.prototype.sendEvent = function () {}

  XServer.prototype.write = function (client, data) {
    if (! this.clients[client.id])
      throw new Error('Invalid client! Disconnected?');
    if (! (data instanceof EndianBuffer))
      throw new Error('Not a buffer!');
    var buffer = new EndianBuffer(data.length + 2);
    buffer.endian = true; // Client id is always Little Endian
    data.copy(buffer, 2);
    buffer.writeUInt16(client.id, 0);
    this.socket.send(buffer.buffer);
  }

  XServer.prototype.processData = function (buffer) {
    var id = buffer.readUInt16(0)
      , data = buffer.slice(2);

    if (! this.clients[id])
      throw new Error('Invalid client! Disconnected?');
    this.clients[id].processData(data);
  }

  XServer.prototype.getResource = function (id, Type, allowed_values) {
    var resource = this.resources[id];
    allowed_values = Array.isArray(allowed_values) ?
      allowed_values : Array.prototype.slice.call(arguments, 2);
    if (~ allowed_values.indexOf(id))
      return id;
    if (Type && !(resource instanceof Type))
      throw new x_types.Error({}, Type.error_code || 1, id);
    return resource;
  }

  XServer.prototype.putResource = function (resource) {
    var owner = resource.owner;
    console.warn('server.putResource', resource.id, resource);
    if (((~ owner.resource_id_mask) & owner.resource_id_base) !== owner.resource_id_base)
      throw new x_types.Error({}, 14 /* IDChoise */, resource.id);
    if (this.resources[resource.id])
      throw new x_types.Error({}, 14 /* IDChoice */, resource.id);
    return this.resources[resource.id] = resource;
  }

  XServer.prototype.freeResource = function (id, Type) {
    var resource = this.resources[id];
    console.warn('server.freeResource', id, resource);
    if (Type && !(resource instanceof Type))
      throw new x_types.Error({}, Type.error_code || 1, id);
    delete this.resources[id];
  }

  XServer.prototype.getAtom = function (id) {
    var atom = this.atoms[id];
    if (!(atom instanceof x_types.Atom))
      throw new x_types.Error({}, x_types.Atom.error_code);
    return atom;
  }

  XServer.prototype.flushGrabBuffer = function() {
    var item = null;
    while (item = this.grab_buffer.shift())
      item[0][item[1]].apply(item[0], item.slice(2));
    Object.keys(this.clients).forEach(function (id) {
      this.clients[id].reqs_processing = false;
      this.clients[id].processReqs();
    }.bind(this));
  }

  XServer.prototype.listFonts = function (pattern) {
    var re = new RegExp('^' + pattern.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1").replace(/\\([*?])/g, '.$1') + '$', 'i');
    return Object.keys(this.fonts_dir).filter(function(name) { return re.test(name) });
  }

  XServer.prototype.resolveFont = function (name) {
    var resolved_name;
    if (! (name in this.fonts_dir)) {
      if (/[*?]/.test(name)) {
        var names = this.listFonts(name);
        if (names && names.length)
          resolved_name = [names[0], this.fonts_dir[names[0]]];
      }
    }
    return resolved_name || [name, this.fonts_dir[name]];
  }

  XServer.prototype.loadFont = function (resolved_name, server_name, callback) {
    var self = this;
    console.log('XServer.loadFont', [].slice.call(arguments));
    console.log('Font cached?', resolved_name in this.fonts_cache);
    if (resolved_name in this.fonts_cache)
      return callback(null, this.fonts_cache[resolved_name]);
    self.grabbed = 'loadFont';
    fs.readFile('fonts/' + resolved_name + '.meta.json', 'utf8', function (err, meta) {
      console.log('read meta');
      if (err)
        return callback(err);
      try {
        meta = JSON.parse(meta);
      } catch (e) {
        return callback(e);
      }
      var font = new x_types.Font(meta.type, resolved_name, server_name);
      font.meta = meta;
      font.loadData(function (err) {
        self.grabbed = null;
        console.log('XServer.loadFont callback', [].slice.call(arguments));
        if (!err) {
          console.log('Font loaded', font);
        }
        self.fonts_cache[resolved_name] = font;
        console.log('Font loaded2', font);
        callback(err, font);
      });
    });
  }

  XServer.prototype.openFont = function (client, fid, name) {
    var self = this
      , resolved_name = this.resolveFont(name);
    if (resolved_name[1]) {
      this.grab = 'openFont';
      this.loadFont(resolved_name[1], resolved_name[0], function (err, font) {
        font.id = fid;
        font.owner = client;
        if (err)
          font.error = err;
        else
          self.putResource(font);
        console.log('XServer.openFont callback', err, font);
        self.grab = false;
        self.flushGrabBuffer();
      });
      return;
    }
    console.log('Name not resolved', name);
    this.grab = false;
    this.flushGrabBuffer();

  }

  XServer.prototype.encodeString = function (str) {
    var out_str = '';
    if (typeof str !== 'string')
      return str;
    for (var i = 0; i < str.length; i ++) {
      var v = str.charCodeAt(i);
      if (v < 0x21) {
        out_str += String.fromCharCode(0x2400 + v);
        continue;
      }
      if (v > 0x7e && v < 0xa1) {
        out_str += String.fromCharCode(0xf800 - 0x7e + v);
        continue;
      }
      out_str += str.charAt(i);
    }
    return out_str;
  }

  return XServer;
});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { hashHistory } from 'react-router'

import DerContainer from './../components/DerContainer/DerContainer.js'
import Message from './../components/Message/Message.js'
import Button from './../components/Button/Button.js'
import Loader from './../components/Loader/Loader.js'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mode: this.props.route.config.defaultMode,
      searchableElement: null,
      openedMenu: ''
    }
  }

  componentWillMount() {
    this.props.setOption('tts', this.props.route.config.tts)
    this.props.setOption('exit', {fn: this.props.route.config.exit})
    this.props.initConfig({format: this.props.route.config.format || this.props.config.format})
    if (this.props.derFile === null && this.props.route.config.derFile !== null) {
      this.props.setDerFile(this.props.route.config.derFile)
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.routes[nextProps.routes.length-1].path === 'file') {
      this.context.router.push('/menu')
    }
  }

  toggleMenu(id) {
    const route = this.props.routes[this.props.routes.length-1].path
    if (route === id) {
      hashHistory.push('/')
      this.read('Fermeture du menu')
    } else {
      hashHistory.push(id)
      this.read('Ouverture du menu')
    }
  }

  showMessage(text, type) {
    this.props.setMessage({text: text, type: type})
    this.read(text)
  }

  changeDerFile(file) {
    if (file !== null) {
      this.props.isLoading(true)
      this.context.router.push('/')
      this.props.setDerFile(file)
      this.read('Chargement du document').then(() => {
        this.props.isLoading(false)
        this.context.router.push('/')
      })
    } else {
      this.context.router.push('/menu')
    }
  }

  changeMode(mode) {
    this.setState({mode: mode})
  }

  setSearchableElement(searchableElement) {
    this.setState({searchableElement: searchableElement})
  }

  getMessage() {
    const {message} = this.props
    if (message && !!message.text) {
      return <Message text={message.text} type={message.messageType} />
    }
  }

  read(text) {
    if (this.props.config.tts) {
      return this.props.config.tts.speak(text)
    }
  }

  render() {
    const { mode, searchableElement } = this.state
    const { config, der, selectedDocument, derFile, activeFilter } = this.props
    // const route = this.props.routes[this.props.routes.length-1].path
    const pathname = this.props.routing.locationBeforeTransitions.pathname

    let navigation
    if (this.props.children) {
      navigation = React.cloneElement(this.props.children, {
        config: config,
        options: {
          der, selectedDocument, derFile, activeFilter
        },
        actions: {
          toggleMenu: this.toggleMenu.bind(this),
          showMessage: (text, type) => this.showMessage(text, type),
          setFilesList: files => this.props.setFilesList(files),
          setDer: der => this.props.setDer(der),
          changeDerFile: file => this.changeDerFile(file),
          changeDocument: this.changeDocument,
          changeMode: this.changeMode,
          changeFilter: filter => this.props.setFilter(filter),
          setSearchableElement: this.setSearchableElement,
          setOptionFormat: (format) => this.props.setOptionFormat(format)
        }
      })
    }

    if (this.props.loading || !this.props.config.tts) {
      return <Loader/>
    }

    return (

        <Button
          id="menu"
          config={config}
          labelClosed="Menu"
          labelOpened="Fermer le menu"
          open={pathname.includes('menu')}
          toggleMenu={this.toggleMenu.bind(this)}
           />
        {
          this.props.der !== null
          ? <Button
              id="filters"
              config={config}
              labelClosed="Filtres"
              labelOpened="Fermer les filtres"
              open={pathname.includes('filters')}
              toggleMenu={this.toggleMenu.bind(this)}
            />
          : null
        }

        {this.getMessage()}

        {
          pathname === '/'
          ? <DerContainer
              setFilesList={files => this.props.setFilesList(files)}
              setDer={der => this.props.setDer(der)}
              der={der}
              selectedDocument={selectedDocument}
              searchableElement={searchableElement}
              message={(text, type) => this.showMessage(text, type)}
              tts={config.tts}
              mode={mode}
              filter={activeFilter}
              derFile={derFile}
            />
          : null
        }

        { navigation || '' }

    )
  }
}

App.contextTypes = {
  router: PropTypes.object.isRequired
}

export default App<commit_msg>Remove 'ouverture / fermeture' tts #74<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { hashHistory } from 'react-router'

import DerContainer from './../components/DerContainer/DerContainer.js'
import Message from './../components/Message/Message.js'
import Button from './../components/Button/Button.js'
import Loader from './../components/Loader/Loader.js'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      mode: this.props.route.config.defaultMode,
      searchableElement: null,
      openedMenu: ''
    }
  }

  componentWillMount() {
    this.props.setOption('tts', this.props.route.config.tts)
    this.props.setOption('exit', {fn: this.props.route.config.exit})
    this.props.initConfig({format: this.props.route.config.format || this.props.config.format})
    if (this.props.derFile === null && this.props.route.config.derFile !== null) {
      this.props.setDerFile(this.props.route.config.derFile)
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.routes[nextProps.routes.length-1].path === 'file') {
      this.context.router.push('/menu')
    }
  }

  toggleMenu(id) {
    const route = this.props.routes[this.props.routes.length-1].path
    if (route === id) {
      hashHistory.push('/')
      // this.read('Fermeture du menu')
    } else {
      hashHistory.push(id)
      // this.read('Ouverture du menu')
    }
  }

  showMessage(text, type) {
    this.props.setMessage({text: text, type: type})
    this.read(text)
  }

  changeDerFile(file) {
    if (file !== null) {
      this.props.isLoading(true)
      this.context.router.push('/')
      this.props.setDerFile(file)
      this.read('Chargement du document').then(() => {
        this.props.isLoading(false)
        this.context.router.push('/')
      })
    } else {
      this.context.router.push('/menu')
    }
  }

  changeMode(mode) {
    this.setState({mode: mode})
  }

  setSearchableElement(searchableElement) {
    this.setState({searchableElement: searchableElement})
  }

  getMessage() {
    const {message} = this.props
    if (message && !!message.text) {
      return <Message text={message.text} type={message.messageType} />
    }
  }

  read(text) {
    if (this.props.config.tts) {
      return this.props.config.tts.speak(text)
    }
  }

  render() {
    const { mode, searchableElement } = this.state
    const { config, der, selectedDocument, derFile, activeFilter } = this.props
    // const route = this.props.routes[this.props.routes.length-1].path
    const pathname = this.props.routing.locationBeforeTransitions.pathname

    let navigation
    if (this.props.children) {
      navigation = React.cloneElement(this.props.children, {
        config: config,
        options: {
          der, selectedDocument, derFile, activeFilter
        },
        actions: {
          toggleMenu: this.toggleMenu.bind(this),
          showMessage: (text, type) => this.showMessage(text, type),
          setFilesList: files => this.props.setFilesList(files),
          setDer: der => this.props.setDer(der),
          changeDerFile: file => this.changeDerFile(file),
          changeDocument: this.changeDocument,
          changeMode: this.changeMode,
          changeFilter: filter => this.props.setFilter(filter),
          setSearchableElement: this.setSearchableElement,
          setOptionFormat: (format) => this.props.setOptionFormat(format)
        }
      })
    }

    if (this.props.loading || !this.props.config.tts) {
      return <Loader/>
    }

    return (

        <Button
          id="menu"
          config={config}
          labelClosed="Menu"
          labelOpened="Fermer le menu"
          open={pathname.includes('menu')}
          toggleMenu={this.toggleMenu.bind(this)}
           />
        {
          this.props.der !== null
          ? <Button
              id="filters"
              config={config}
              labelClosed="Filtres"
              labelOpened="Fermer les filtres"
              open={pathname.includes('filters')}
              toggleMenu={this.toggleMenu.bind(this)}
            />
          : null
        }

        {this.getMessage()}

        {
          pathname === '/'
          ? <DerContainer
              setFilesList={files => this.props.setFilesList(files)}
              setDer={der => this.props.setDer(der)}
              der={der}
              selectedDocument={selectedDocument}
              searchableElement={searchableElement}
              message={(text, type) => this.showMessage(text, type)}
              tts={config.tts}
              mode={mode}
              filter={activeFilter}
              derFile={derFile}
            />
          : null
        }

        { navigation || '' }

    )
  }
}

App.contextTypes = {
  router: PropTypes.object.isRequired
}

export default App<|endoftext|>


<commit_before>// Copyright 2017-2018, University of Colorado Boulder

/**
 * Base type for instrumented PhET-iO instances, provides support for PhET-iO features when running with PhET-iO enabled.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var tandemNamespace = require( 'TANDEM/tandemNamespace' );
  var Tandem = require( 'TANDEM/Tandem' );

  // ifphetio
  var ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  var dataStream = require( 'ifphetio!PHET_IO/dataStream' );

  // constants
  var PHET_IO_ENABLED = !!( window.phet && window.phet.phetio );

  // Flag that indicates a high frequency message was skipped.
  var SKIPPING_HIGH_FREQUENCY_MESSAGE = -1;

  var DEFAULTS = {
    tandem: Tandem.optional,      // By default tandems are optional, but subtypes can specify this as
                                  // `Tandem.tandemRequired` to enforce its presence
    phetioType: ObjectIO,         // Supply the appropriate IO type
    phetioDocumentation: null,    // Useful notes about an instrumented instance, shown in the PhET-iO Studio Wrapper

    phetioState: true,            // When true, includes the instance in the PhET-iO state
    phetioReadOnly: false,        // When true, you can only get values from the instance; no setting allowed.
    phetioEventType: 'model',     // Default event type for this instance, can be overridden in phetioStartEvent options
    phetioHighFrequency: false,   // This instance emits events that are high frequency events such as mouse moves or
                                  // stepSimulation can be omitted from data stream
    phetioPlayback: false         // This instance emits events that are only needed for data streams intended for playback,
                                  // and otherwise can be suppressed.
  };

  var OPTIONS_KEYS = _.keys( DEFAULTS );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function PhetioObject( options ) {

    // @public (read-only) {Tandem} - assigned in initializePhetioObject - the unique tandem for this instance
    this.tandem = null;

    // @public (read-only) {IOType} - assigned in initializePhetioObject - the IO type associated with this instance
    this.phetioType = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, included in the PhET-iO state
    this.phetioState = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, values can be get but not set
    this.phetioReadOnly = null;

    // @public (read-only) {string} - assigned in initializePhetioObject - Notes about an instance, shown in the PhET-iO Studio Wrapper
    this.phetioDocumentation = null;

    // @public (read-only) {Object} - assigned in initializePhetioObject - The wrapper instance for PhET-iO interoperation
    this.phetioWrapper = null;

    // @private {boolean} - track whether the object has been initialized.  This is necessary because initialization
    // can happen in the constructor or in a subsequent call to initializePhetioObject (to support scenery Node)
    this.phetioObjectInitialized = false;

    // @private {number|null} - tracks the indices of started messages so that dataStream can check that ends match starts
    this.phetioMessageStack = [];

    // @private {boolean} - has the instance been disposed?
    this.phetioObjectDisposed = false;

    // @private {string} - 'model' | 'user'
    this.phetioEventType = null;

    // @private {boolean} - If marked as phetioHighFrequency: true, the event will be omitted when the query parameter phetioEmitHighFrequencyEvents=false, also see option in Client.launchSim()
    this.phetioHighFrequency = null;

    // @private {boolean} - This indicates a (usually high-frequency) event that is required for
    // visual playbacks, but can be otherwise overwhelming.  For instance, frameEndedEmitter emits dt's that are critical to playbacks
    // but not helpful when reading console: colorized.
    this.phetioPlayback = null;

    if ( options ) {
      this.initializePhetioObject( {}, options );
    }
  }

  tandemNamespace.register( 'PhetioObject', PhetioObject );

  return inherit( Object, PhetioObject, {

    /**
     * @param {Object} baseOptions - only applied if options keys intersect OPTIONS_KEYS
     * @param {Object} options
     * @protected
     */
    initializePhetioObject: function( baseOptions, options ) {
      assert && assert( options, 'initializePhetioObject must be called with options' );

      // TODO: garbage-free implementation
      var intersection = _.intersection( _.keys( options ), OPTIONS_KEYS );
      if ( intersection.length === 0 ) {
        return; // no PhetioObject keys provided, perhaps they will be provided in a subsequent mutate call.
      }
      assert && assert( !this.phetioObjectInitialized, 'cannot initialize twice' );

      assert && assert( options.tandem, 'Component was missing its tandem' );
      assert && assert( options.tandem.phetioID, 'Component was missing its phetioID' );

      if ( assert && options.phetioType && PHET_IO_ENABLED ) {
        assert && assert( options.phetioType.documentation, 'There must be a documentation string for each IO Type.' );

        for ( var methodName in options.phetioType.methods ) {
          var method = options.phetioType.methods[ methodName ];

          if ( typeof method === 'function' ) {

            // This is a private function for internal phet-io mechanics, not for exporting over the API, so it doesn't
            // need to be checked.
          }
          else {
            var IOType = options.phetioType;

            // If you get one of these assertion errors, go to the IOType definition file and check its methods
            assert && assert( !!method.returnType, IOType.typeName + '.' + methodName + ' needs a returnType' );
            assert && assert( !!method.implementation, IOType.typeName + '.' + methodName + ' needs an implementation function' );
            assert && assert( !!method.parameterTypes, IOType.typeName + '.' + methodName + ' needs a parameterTypes array' );
            assert && assert( !!method.documentation, IOType.typeName + '.' + methodName + ' needs a documentation string' );
          }
        }

        assert && assert( options.phetioType !== undefined, options.tandem.phetioID + ' missing type from phetio.api' );
        assert && assert( options.phetioType.typeName, 'no type name for ' + options.tandem.phetioID + '(may be missing type parameter)' );
        assert && assert( options.phetioType.typeName, 'type must be specified and have a typeName for ' + options.tandem.phetioID );
      }

      options = _.extend( {}, DEFAULTS, baseOptions, options );

      // Unpack options to instance properties
      this.tandem = options.tandem;
      this.phetioType = options.phetioType;
      this.phetioState = options.phetioState;
      this.phetioReadOnly = options.phetioReadOnly;
      this.phetioEventType = options.phetioEventType;
      this.phetioDocumentation = options.phetioDocumentation;
      this.phetioHighFrequency = options.phetioHighFrequency;
      this.phetioPlayback = options.phetioPlayback;

      // Instantiate the wrapper instance which is used for PhET-iO communication
      if ( PHET_IO_ENABLED && this.tandem.supplied ) {
        // this assertion should be enabled for new phet-io sim publications
        // assert && assert( this.phetioDocumentation, 'Instance documentation is required for: ' + this.tandem.phetioID );
        this.phetioWrapper = new this.phetioType( this, this.tandem.phetioID );
      }

      // Register with the tandem registry
      this.tandem.addInstance( this );

      this.phetioObjectInitialized = true;
    },

    /**
     * Start an event for the nested PhET-iO data stream.
     *
     * @param {string} event - the name of the event
     * @param {Object|function} [args] - arguments for the event, either an object, or a function that returns an object
     * @public
     */
    phetioStartEvent: function( event, args ) {
      assert && assert( this.phetioObjectInitialized, 'phetioObject should be initialized' );
      assert && assert( typeof event === 'string' );
      assert && args && assert( typeof args === 'object' || typeof args === 'function' );
      assert && assert( arguments.length !== 3, 'Prevent usage of incorrect signature' );

      // Opt out of certain events if queryParameter override is provided
      if ( window.phet && window.phet.phetio ) {
        var omit = !window.phet.phetio.queryParameters.phetioEmitHighFrequencyEvents && this.phetioHighFrequency;
        var omit2 = !window.phet.phetio.queryParameters.phetioEmitPlaybackEvents && this.phetioPlayback;
        if ( omit || omit2 ) {
          this.phetioMessageStack.push( SKIPPING_HIGH_FREQUENCY_MESSAGE );
          return;
        }
      }

      if ( this.isPhetioInstrumented() ) {

        // Only get the args if we are actually going to send the event.
        if ( typeof args === 'function' ) {
          args = args();
        }
        this.phetioMessageStack.push( dataStream.start( this.phetioEventType, this, event, args ) );
      }
    },

    /**
     * End an event on the nested PhET-iO data stream.
     * It this object was disposed or dataStream.start was not called, this is a no-op.
     * @public
     */
    phetioEndEvent: function() {

      var topMessageIndex = this.phetioMessageStack.pop();

      // The message was started as a high frequency event to be skipped, so the end is a no-op
      if ( topMessageIndex === SKIPPING_HIGH_FREQUENCY_MESSAGE ) {
        return;
      }

      if ( this.isPhetioInstrumented() ) {
        dataStream.end( topMessageIndex );
      }
    },

    /**
     * Just because a tandem is passed to an instance doesn't mean that it is instrumented. An instance will only be
     * instrumented if:
     * (1) Running in PhET-iO mode
     * (2) The tandem that was passed in was "supplied". See Tandem.supplied for more info
     * @returns {boolean}
     * @public
     */
    isPhetioInstrumented: function() {
      return this.tandem && this.tandem.supplied && PHET_IO_ENABLED;
    },

    /**
     * JSONifiable metadata that describes the nature of the PhetioObject.
     * @returns {Object}
     * @public
     */
    getMetadata: function() {
      return {
        phetioTypeName: this.phetioType.typeName,
        phetioState: this.phetioState,
        phetioReadOnly: this.phetioReadOnly,
        phetioDocumentation: this.phetioDocumentation,
        phetioEventType: this.phetioEventType,
        phetioHighFrequency: this.phetioHighFrequency,
        phetioPlayback: this.phetioPlayback
      };
    },

    /**
     * Unregisters from tandem when longer used.
     * @public
     */
    dispose: function() {
      var self = this;
      assert && assert( !this.phetioObjectDisposed, 'PhetioObject can only be disposed once' );

      // In order to support the structured data stream, PhetioObjects must end the messages in the correct
      // sequence, without being interrupted by dispose() calls.  Therefore, we do not clear out any of the state
      // related to the endEvent.  Note this means it is acceptable (and expected) for endEvent() to be called on disposed PhetioObjects.
      //
      // The phetioEvent stack should resolve by the next clock tick, so that's when we check it.
      assert && setTimeout( function() {
        assert && assert( self.phetioMessageStack.length === 0, 'phetioMessageStack should be clear' );
      }, 0 );

      // OK to dispose something that was never phetioObjectInitialized, this means it was an uninstrumented instance
      if ( this.phetioObjectInitialized ) {

        // Tandem de-registration
        this.tandem.removeInstance( this );
        this.phetioWrapper && this.phetioWrapper.dispose && this.phetioWrapper.dispose();
      }

      this.phetioObjectDisposed = true;
    }
  }, {
    DEFAULT_OPTIONS: DEFAULTS // the default options for the phet-io object
  } );
} );<commit_msg>Identify playback events and leverage them for playback, see https://github.com/phetsims/phet-io/issues/1383<commit_after>// Copyright 2017-2018, University of Colorado Boulder

/**
 * Base type for instrumented PhET-iO instances, provides support for PhET-iO features when running with PhET-iO enabled.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var tandemNamespace = require( 'TANDEM/tandemNamespace' );
  var Tandem = require( 'TANDEM/Tandem' );

  // ifphetio
  var ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  var dataStream = require( 'ifphetio!PHET_IO/dataStream' );

  // constants
  var PHET_IO_ENABLED = !!( window.phet && window.phet.phetio );

  // Flag that indicates a high frequency message was skipped.
  var SKIPPING_HIGH_FREQUENCY_MESSAGE = -1;

  var DEFAULTS = {
    tandem: Tandem.optional,      // By default tandems are optional, but subtypes can specify this as
                                  // `Tandem.tandemRequired` to enforce its presence
    phetioType: ObjectIO,         // Supply the appropriate IO type
    phetioDocumentation: null,    // Useful notes about an instrumented instance, shown in the PhET-iO Studio Wrapper

    phetioState: true,            // When true, includes the instance in the PhET-iO state
    phetioReadOnly: false,        // When true, you can only get values from the instance; no setting allowed.
    phetioEventType: 'model',     // Default event type for this instance, can be overridden in phetioStartEvent options
    phetioHighFrequency: false,   // This instance emits events that are high frequency events such as mouse moves or
                                  // stepSimulation can be omitted from data stream
    phetioPlayback: false         // This instance emits events that are only needed for data streams intended for playback,
                                  // and otherwise can be suppressed.
  };

  var OPTIONS_KEYS = _.keys( DEFAULTS );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function PhetioObject( options ) {

    // @public (read-only) {Tandem} - assigned in initializePhetioObject - the unique tandem for this instance
    this.tandem = null;

    // @public (read-only) {IOType} - assigned in initializePhetioObject - the IO type associated with this instance
    this.phetioType = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, included in the PhET-iO state
    this.phetioState = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, values can be get but not set
    this.phetioReadOnly = null;

    // @public (read-only) {string} - assigned in initializePhetioObject - Notes about an instance, shown in the PhET-iO Studio Wrapper
    this.phetioDocumentation = null;

    // @public (read-only) {Object} - assigned in initializePhetioObject - The wrapper instance for PhET-iO interoperation
    this.phetioWrapper = null;

    // @private {boolean} - track whether the object has been initialized.  This is necessary because initialization
    // can happen in the constructor or in a subsequent call to initializePhetioObject (to support scenery Node)
    this.phetioObjectInitialized = false;

    // @private {number|null} - tracks the indices of started messages so that dataStream can check that ends match starts
    this.phetioMessageStack = [];

    // @private {boolean} - has the instance been disposed?
    this.phetioObjectDisposed = false;

    // @private {string} - 'model' | 'user'
    this.phetioEventType = null;

    // @private {boolean} - If marked as phetioHighFrequency: true, the event will be omitted when the query parameter phetioEmitHighFrequencyEvents=false, also see option in Client.launchSim()
    this.phetioHighFrequency = null;

    // @private {boolean} - This indicates a (usually high-frequency) event that is required for
    // visual playbacks, but can be otherwise overwhelming.  For instance, stepSimulationEmitter emits dt's that are critical to playbacks
    // but not helpful when reading console: colorized.
    this.phetioPlayback = null;

    if ( options ) {
      this.initializePhetioObject( {}, options );
    }
  }

  tandemNamespace.register( 'PhetioObject', PhetioObject );

  return inherit( Object, PhetioObject, {

    /**
     * @param {Object} baseOptions - only applied if options keys intersect OPTIONS_KEYS
     * @param {Object} options
     * @protected
     */
    initializePhetioObject: function( baseOptions, options ) {
      assert && assert( options, 'initializePhetioObject must be called with options' );

      // TODO: garbage-free implementation
      var intersection = _.intersection( _.keys( options ), OPTIONS_KEYS );
      if ( intersection.length === 0 ) {
        return; // no PhetioObject keys provided, perhaps they will be provided in a subsequent mutate call.
      }
      assert && assert( !this.phetioObjectInitialized, 'cannot initialize twice' );

      assert && assert( options.tandem, 'Component was missing its tandem' );
      assert && assert( options.tandem.phetioID, 'Component was missing its phetioID' );

      if ( assert && options.phetioType && PHET_IO_ENABLED ) {
        assert && assert( options.phetioType.documentation, 'There must be a documentation string for each IO Type.' );

        for ( var methodName in options.phetioType.methods ) {
          var method = options.phetioType.methods[ methodName ];

          if ( typeof method === 'function' ) {

            // This is a private function for internal phet-io mechanics, not for exporting over the API, so it doesn't
            // need to be checked.
          }
          else {
            var IOType = options.phetioType;

            // If you get one of these assertion errors, go to the IOType definition file and check its methods
            assert && assert( !!method.returnType, IOType.typeName + '.' + methodName + ' needs a returnType' );
            assert && assert( !!method.implementation, IOType.typeName + '.' + methodName + ' needs an implementation function' );
            assert && assert( !!method.parameterTypes, IOType.typeName + '.' + methodName + ' needs a parameterTypes array' );
            assert && assert( !!method.documentation, IOType.typeName + '.' + methodName + ' needs a documentation string' );
          }
        }

        assert && assert( options.phetioType !== undefined, options.tandem.phetioID + ' missing type from phetio.api' );
        assert && assert( options.phetioType.typeName, 'no type name for ' + options.tandem.phetioID + '(may be missing type parameter)' );
        assert && assert( options.phetioType.typeName, 'type must be specified and have a typeName for ' + options.tandem.phetioID );
      }

      options = _.extend( {}, DEFAULTS, baseOptions, options );

      // Unpack options to instance properties
      this.tandem = options.tandem;
      this.phetioType = options.phetioType;
      this.phetioState = options.phetioState;
      this.phetioReadOnly = options.phetioReadOnly;
      this.phetioEventType = options.phetioEventType;
      this.phetioDocumentation = options.phetioDocumentation;
      this.phetioHighFrequency = options.phetioHighFrequency;
      this.phetioPlayback = options.phetioPlayback;

      // Instantiate the wrapper instance which is used for PhET-iO communication
      if ( PHET_IO_ENABLED && this.tandem.supplied ) {
        // this assertion should be enabled for new phet-io sim publications
        // assert && assert( this.phetioDocumentation, 'Instance documentation is required for: ' + this.tandem.phetioID );
        this.phetioWrapper = new this.phetioType( this, this.tandem.phetioID );
      }

      // Register with the tandem registry
      this.tandem.addInstance( this );

      this.phetioObjectInitialized = true;
    },

    /**
     * Start an event for the nested PhET-iO data stream.
     *
     * @param {string} event - the name of the event
     * @param {Object|function} [args] - arguments for the event, either an object, or a function that returns an object
     * @public
     */
    phetioStartEvent: function( event, args ) {
      assert && assert( this.phetioObjectInitialized, 'phetioObject should be initialized' );
      assert && assert( typeof event === 'string' );
      assert && args && assert( typeof args === 'object' || typeof args === 'function' );
      assert && assert( arguments.length !== 3, 'Prevent usage of incorrect signature' );

      // Opt out of certain events if queryParameter override is provided
      if ( window.phet && window.phet.phetio ) {
        var omit = !window.phet.phetio.queryParameters.phetioEmitHighFrequencyEvents && this.phetioHighFrequency;
        var omit2 = !window.phet.phetio.queryParameters.phetioEmitPlaybackEvents && this.phetioPlayback;
        if ( omit || omit2 ) {
          this.phetioMessageStack.push( SKIPPING_HIGH_FREQUENCY_MESSAGE );
          return;
        }
      }

      if ( this.isPhetioInstrumented() ) {

        // Only get the args if we are actually going to send the event.
        if ( typeof args === 'function' ) {
          args = args();
        }
        this.phetioMessageStack.push( dataStream.start( this.phetioEventType, this, event, args ) );
      }
    },

    /**
     * End an event on the nested PhET-iO data stream.
     * It this object was disposed or dataStream.start was not called, this is a no-op.
     * @public
     */
    phetioEndEvent: function() {

      var topMessageIndex = this.phetioMessageStack.pop();

      // The message was started as a high frequency event to be skipped, so the end is a no-op
      if ( topMessageIndex === SKIPPING_HIGH_FREQUENCY_MESSAGE ) {
        return;
      }

      if ( this.isPhetioInstrumented() ) {
        dataStream.end( topMessageIndex );
      }
    },

    /**
     * Just because a tandem is passed to an instance doesn't mean that it is instrumented. An instance will only be
     * instrumented if:
     * (1) Running in PhET-iO mode
     * (2) The tandem that was passed in was "supplied". See Tandem.supplied for more info
     * @returns {boolean}
     * @public
     */
    isPhetioInstrumented: function() {
      return this.tandem && this.tandem.supplied && PHET_IO_ENABLED;
    },

    /**
     * JSONifiable metadata that describes the nature of the PhetioObject.
     * @returns {Object}
     * @public
     */
    getMetadata: function() {
      return {
        phetioTypeName: this.phetioType.typeName,
        phetioState: this.phetioState,
        phetioReadOnly: this.phetioReadOnly,
        phetioDocumentation: this.phetioDocumentation,
        phetioEventType: this.phetioEventType,
        phetioHighFrequency: this.phetioHighFrequency,
        phetioPlayback: this.phetioPlayback
      };
    },

    /**
     * Unregisters from tandem when longer used.
     * @public
     */
    dispose: function() {
      var self = this;
      assert && assert( !this.phetioObjectDisposed, 'PhetioObject can only be disposed once' );

      // In order to support the structured data stream, PhetioObjects must end the messages in the correct
      // sequence, without being interrupted by dispose() calls.  Therefore, we do not clear out any of the state
      // related to the endEvent.  Note this means it is acceptable (and expected) for endEvent() to be called on disposed PhetioObjects.
      //
      // The phetioEvent stack should resolve by the next clock tick, so that's when we check it.
      assert && setTimeout( function() {
        assert && assert( self.phetioMessageStack.length === 0, 'phetioMessageStack should be clear' );
      }, 0 );

      // OK to dispose something that was never phetioObjectInitialized, this means it was an uninstrumented instance
      if ( this.phetioObjectInitialized ) {

        // Tandem de-registration
        this.tandem.removeInstance( this );
        this.phetioWrapper && this.phetioWrapper.dispose && this.phetioWrapper.dispose();
      }

      this.phetioObjectDisposed = true;
    }
  }, {
    DEFAULT_OPTIONS: DEFAULTS // the default options for the phet-io object
  } );
} );<|endoftext|>


<commit_before>(function () {

function Preprocessor (options) {

  options = options || {};

  var
    data;

  this.getData = function () {
    return data;
  }

  this.setData = function (newData) {
    var
      i, length;
    if (!_.isArray(newData)) throw new Error('Array expected.');
    if (newData.length < 2) throw new Error('Data must contain at least two dimensions.');
    length = newData[0].length;
    for (i = newData.length; i--;) {
      if (!_.isArray(newData[i])) throw new Error('Data dimensions must be arrays.');
      if (newData[i].length !== length) throw new Error('Data dimensions must contain the same number of points.');
    }

    data = newData;
  }

  if (options.data) this.setData(options.data);
}

Preprocessor.prototype = {

  length : function () {
    return this.getData()[0].length;
  },

  bound : function (min, max) {

    if (!_.isNumber(min) || !_.isNumber(max)) return this;

    var
      data    = this.getData(),
      length  = this.length(),
      x       = data[0],
      y       = data[1],
      i, index;

    for (i = 0; i < length; i++) {
      if (x[i] >= min) break;
    }

    index = i;
    if (i > 0) i--;

    for (i; i < length; i++) {
      if (x[i] > max) break;
    }

    this.setData([
      x.slice(index, i),
      y.slice(index, i)
    ]);

    return this;
  },

  /**
   * Sample using min and max.
   */
  subsampleMinMax : function (resolution) {

    var
      data    = this.getData(),
      length  = this.length(),
      count   = (resolution - 2) / 2,
      x       = data[0],
      y       = data[1],
      newX    = [],
      newY    = [],
      min     = Number.MAX_VALUE,
      max     = -Number.MAX_VALUE,
      minI    = 1,
      maxI    = 1,
      unit    = Math.floor(length / count),
      position, min, max, datum, i, j;

    if (length > resolution) {

      newX.push(x[0]);
      newY.push(y[0]);

      position = unit;

      for (i = 1; i < length - 1; i++) {

        if (i === position) {

          position += unit;

          j = Math.min(maxI, minI);
          newX.push(x[j]);
          newY.push(y[j]);

          j = Math.max(maxI, minI);
          newX.push(x[j]);
          newY.push(y[j]);

          minI = i;
          min = y[minI];
          maxI = i;
          max = y[maxI];

        } else {
          if (y[i] > max) {
            max = y[i];
            maxI = i;
          }

          if (y[i] < min) {
            min = y[i];
            minI = i;
          }
        }
      }

      if (i < position) {
        newX.push(x[minI]);
        newY.push(min);
        newX.push(x[maxI]);
        newY.push(max);
      }

      // Last
      newX.push(x[length-1]);
      newY.push(y[length-1]);

      this.setData([newX, newY]);
    }

    return this;
  },

  subsample : function (resolution) {

    var
      data    = this.getData(),
      length  = this.length(),
      unit    = length / resolution,
      x       = data[0],
      y       = data[1],
      newX    = [],
      newY    = [],
      i, index;

    if (length > resolution) {

      // First
      newX.push(x[0]);
      newY.push(y[0]);

      for (i = 1; i < resolution; i++) {
        if (i * unit >= length - unit)
          break;
        index = Math.round(i * unit);
        newX.push(x[index]);
        newY.push(y[index]);
      }

      // Last
      newX.push(x[length-1]);
      newY.push(y[length-1]);

      this.setData([newX, newY]);
    }

    return this;
  }
};

humblevis.Preprocessor = Preprocessor;

}());
<commit_msg>Make bound so that it can be composited more efficiently.<commit_after>(function () {

function Preprocessor (options) {

  options = options || {};

  var
    data;

  this.getData = function () {
    if (this.bounded === false) bound(this);
    return data;
  }

  this.setData = function (newData) {
    var
      i, length;
    if (!_.isArray(newData)) throw new Error('Array expected.');
    if (newData.length < 2) throw new Error('Data must contain at least two dimensions.');
    length = newData[0].length;
    for (i = newData.length; i--;) {
      if (!_.isArray(newData[i])) throw new Error('Data dimensions must be arrays.');
      if (newData[i].length !== length) throw new Error('Data dimensions must contain the same number of points.');
    }

    data = newData;
  }

  if (options.data) this.setData(options.data);
}

function getStartIndex (data, min) {

  var
    length = data.length,
    i;

  for (i = 0; i < length; i++) {
    if (data[i] >= min) break;
  }

  return i;
}

function getEndIndex (data, max) {

  var
    i;

  for (i = data.length; i--;) {
    if (data[i] <= max) break;
  }

  return i;
}

function bound (that, data) {

  delete that.bounded;

  var
    data    = that.getData(),
    length  = that.length(),
    min     = that.min,
    max     = that.max,
    x       = data[0],
    y       = data[1],
    index   = getStartIndex(x, min),
    i       = index ? index : index - 1;

  for (i; i < length; i++) {
    if (x[i] > max) break;
  }

  that.setData([
    x.slice(index, i),
    y.slice(index, i)
  ]);

  return that;
};

Preprocessor.prototype = {

  length : function () {
    return this.getData()[0].length;
  },

  bound : function (min, max) {

    if (!_.isNumber(min) || !_.isNumber(max)) return this;

    this.min = min;
    this.max = max;
    this.bounded = false;
  },

  /**
   * Sample using min and max.
   */
  subsampleMinMax : function (resolution) {

    var
      data    = this.getData(),
      length  = this.length(),
      count   = (resolution - 2) / 2,
      x       = data[0],
      y       = data[1],
      newX    = [],
      newY    = [],
      min     = Number.MAX_VALUE,
      max     = -Number.MAX_VALUE,
      minI    = 1,
      maxI    = 1,
      unit    = Math.floor(length / count),
      position, min, max, datum, i, j;

    if (length > resolution) {

      newX.push(x[0]);
      newY.push(y[0]);

      position = unit;

      for (i = 1; i < length - 1; i++) {

        if (i === position) {

          position += unit;

          j = Math.min(maxI, minI);
          newX.push(x[j]);
          newY.push(y[j]);

          j = Math.max(maxI, minI);
          newX.push(x[j]);
          newY.push(y[j]);

          minI = i;
          min = y[minI];
          maxI = i;
          max = y[maxI];

        } else {
          if (y[i] > max) {
            max = y[i];
            maxI = i;
          }

          if (y[i] < min) {
            min = y[i];
            minI = i;
          }
        }
      }

      if (i < position) {
        newX.push(x[minI]);
        newY.push(min);
        newX.push(x[maxI]);
        newY.push(max);
      }

      // Last
      newX.push(x[length-1]);
      newY.push(y[length-1]);

      this.setData([newX, newY]);
    }

    return this;
  },

  subsample : function (resolution) {

    var
      data    = this.getData(),
      length  = this.length(),
      unit    = length / resolution,
      x       = data[0],
      y       = data[1],
      newX    = [],
      newY    = [],
      i, index;

    if (length > resolution) {

      // First
      newX.push(x[0]);
      newY.push(y[0]);

      for (i = 1; i < resolution; i++) {
        if (i * unit >= length - unit)
          break;
        index = Math.round(i * unit);
        newX.push(x[index]);
        newY.push(y[index]);
      }

      // Last
      newX.push(x[length-1]);
      newY.push(y[length-1]);

      this.setData([newX, newY]);
    }

    return this;
  }
};

humblevis.Preprocessor = Preprocessor;

}());
<|endoftext|>


<commit_before>//Render a simple toggle button (without icons or anything)
//TODO: not ready for use in simulations, it will need further development & discussion first.
define( function( require ) {
  "use strict";

  var Node = require( 'SCENERY/nodes/Node' );
  var DOM = require( 'SCENERY/nodes/DOM' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  function ToggleButton( content, property, options ) {
    var toggleButton = this;
    options = options || {};
    options.cursor = 'pointer';
    Node.call( this, options );

    this.path = new Rectangle( 0, 0, content.width + 10, content.height + 10, 10, 10, {stroke: 'black', lineWidth: 1, fill: '#e3e980'} );
    this.addChild( this.path );
    content.centerX = this.path.width / 2;
    content.centerY = this.path.height / 2;
    this.addChild( content );
    this.addInputListener( {up: function() { property.value = !property.value; }} );

//    Create a peer for accessibility
    this.addPeer( '<input type="checkbox">', {click: function() {property.value = !property.value;}, label: options.label} );
    property.link( function( value ) {
      _.each( toggleButton.instances, function( instance ) {

        //Make sure accessibility is enabled, then apply the change to the peer
        _.each( instance.peers, function( peer ) {
          peer.element.setAttribute( 'checked', value );
        } );
      } );
    } );
  }

  inherit( Node, ToggleButton );

  return ToggleButton;
} );
<commit_msg>Moved constants to options hash, see #2<commit_after>//Render a simple toggle button (without icons or anything)
//TODO: not ready for use in simulations, it will need further development & discussion first.
define( function( require ) {
  "use strict";

  var Node = require( 'SCENERY/nodes/Node' );
  var DOM = require( 'SCENERY/nodes/DOM' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );

  function ToggleButton( content, property, options ) {
    var toggleButton = this;

    options = _.extend( {
      padX: 10,
      padY: 10,
      cursor: 'pointer'
    }, options );

    options.cursor = 'pointer';
    Node.call( this, options );

    this.path = new Rectangle( 0, 0, content.width + options.padX, content.height + options.padY, 10, 10, {stroke: 'black', lineWidth: 1, fill: '#e3e980'} );
    this.addChild( this.path );
    content.centerX = this.path.width / 2;
    content.centerY = this.path.height / 2;
    this.addChild( content );
    this.addInputListener( {up: function() { property.value = !property.value; }} );

//    Create a peer for accessibility
    this.addPeer( '<input type="checkbox">', {click: function() {property.value = !property.value;}, label: options.label} );
    property.link( function( value ) {
      _.each( toggleButton.instances, function( instance ) {

        //Make sure accessibility is enabled, then apply the change to the peer
        _.each( instance.peers, function( peer ) {
          peer.element.setAttribute( 'checked', value );
        } );
      } );
    } );
  }

  inherit( Node, ToggleButton );

  return ToggleButton;
} );
<|endoftext|>


<commit_before>/*
 * @fileOverview Background service running for the wallet
 */
require(['backend/port',
         'backend/services/lobby',
         'backend/services/obelisk',
         'backend/services/wallet',
         'backend/services/gui',
         'backend/services/ticker',
         'backend/services/mixer',
         'backend/services/notifier',
         'backend/services/ctxmenus'],
function(Port, LobbyService, ObeliskService, WalletService, GuiService, TickerService, MixerService, NotifierService, CtxMenusService) {

function DarkWalletService() {

    var services = {};

    // Backend services
    services.lobby = new LobbyService(this);
    services.obelisk = new ObeliskService(this);
    services.wallet = new WalletService(this);
    services.ctxMenus = new CtxMenusService(this);
    services.gui = new GuiService(this);
    services.ticker = new TickerService(this);
    services.mixer = new MixerService(this);
    services.notifier = new NotifierService(this);

    var servicesStatus = { gateway: 'offline', obelisk: 'offline' };
    this.servicesStatus = servicesStatus;

    /***************************************
    /* Hook up some utility functions
     */

    this.loadIdentity = function(idx) {
        return services.wallet.loadIdentity(idx);
    };

    // Get an identity from the keyring
    this.getIdentity = function(idx) {
        return services.wallet.getIdentity(idx);
    };
    this.getCurrentIdentity = function() {
        return services.wallet.getCurrentIdentity();
    };
    this.getLobbyTransport = function() {
        return services.lobby.getLobbyTransport();
    };

    // Start up history for an address
    this.initAddress = function(walletAddress) { return services.wallet.initAddress(walletAddress); }

    /***************************************
    /* Global communications
     */

    Port.connect('wallet', function(data) {
        // wallet closing
        if (data.type == 'closing') {
            services['obelisk'].disconnect();
        }
    });

    this.connect = function(connectUri) {
        var identity = services.wallet.getCurrentIdentity();
        connectUri = connectUri || identity.connections.servers[identity.connections.selectedServer].address || 'wss://gateway.unsystem.net';
        servicesStatus.gateway = 'connecting';
        services.obelisk.connect(connectUri, function(err) {
            if (err) {
                servicesStatus.gateway = 'error';
            } else {
                servicesStatus.gateway = 'ok';
                services.wallet.handleInitialConnect();
            }
        });
    };
    this.getKeyRing = function() {
        return services.wallet.getKeyRing();
    };

    this.getClient = function() {
        return services.obelisk.getClient();
    };

    this.getService = function(name) {
        return services[name];
    };
}

/***************************************
/* Communications
 */
var sendInternalMessage = function(msg) {
    chrome.runtime.sendMessage(chrome.runtime.id, msg);
};

var addListener = function(callback) {
    chrome.runtime.onMessage.addListener(callback);
};

/***************************************
/* Service instance that will be running in the background page
 */
var service = new DarkWalletService();

/***************************************
/* Bindings for the page window so we can have easy access
 */

window.connect = function(_server) { return service.connect(_server); };

window.loadIdentity = service.loadIdentity;
window.getIdentity = function(idx) { return service.getIdentity(idx); };
window.getCurrentIdentity = service.getCurrentIdentity;

window.getKeyRing = service.getKeyRing;
window.servicesStatus = service.servicesStatus;
window.getLobbyTransport = service.getLobbyTransport;

window.getClient = service.getClient;
window.getService = service.getService;

window.initAddress = function(_w) {return service.initAddress(_w);};

window.addListener = addListener;
window.sendInternalMessage = sendInternalMessage;
});

<commit_msg>initialize obelisk service before others.<commit_after>/*
 * @fileOverview Background service running for the wallet
 */
require(['backend/port',
         'backend/services/lobby',
         'backend/services/obelisk',
         'backend/services/wallet',
         'backend/services/gui',
         'backend/services/ticker',
         'backend/services/mixer',
         'backend/services/notifier',
         'backend/services/ctxmenus'],
function(Port, LobbyService, ObeliskService, WalletService, GuiService, TickerService, MixerService, NotifierService, CtxMenusService) {

function DarkWalletService() {

    var services = {};

    // Backend services
    services.obelisk = new ObeliskService(this);
    services.wallet = new WalletService(this);
    services.ctxMenus = new CtxMenusService(this);
    services.gui = new GuiService(this);
    services.ticker = new TickerService(this);
    services.mixer = new MixerService(this);
    services.notifier = new NotifierService(this);
    services.lobby = new LobbyService(this);

    var servicesStatus = { gateway: 'offline', obelisk: 'offline' };
    this.servicesStatus = servicesStatus;

    /***************************************
    /* Hook up some utility functions
     */

    this.loadIdentity = function(idx) {
        return services.wallet.loadIdentity(idx);
    };

    // Get an identity from the keyring
    this.getIdentity = function(idx) {
        return services.wallet.getIdentity(idx);
    };
    this.getCurrentIdentity = function() {
        return services.wallet.getCurrentIdentity();
    };
    this.getLobbyTransport = function() {
        return services.lobby.getLobbyTransport();
    };

    // Start up history for an address
    this.initAddress = function(walletAddress) { return services.wallet.initAddress(walletAddress); }

    /***************************************
    /* Global communications
     */

    Port.connect('wallet', function(data) {
        // wallet closing
        if (data.type == 'closing') {
            services['obelisk'].disconnect();
        }
    });

    this.connect = function(connectUri) {
        var identity = services.wallet.getCurrentIdentity();
        connectUri = connectUri || identity.connections.servers[identity.connections.selectedServer].address || 'wss://gateway.unsystem.net';
        servicesStatus.gateway = 'connecting';
        services.obelisk.connect(connectUri, function(err) {
            if (err) {
                servicesStatus.gateway = 'error';
            } else {
                servicesStatus.gateway = 'ok';
                services.wallet.handleInitialConnect();
            }
        });
    };
    this.getKeyRing = function() {
        return services.wallet.getKeyRing();
    };

    this.getClient = function() {
        return services.obelisk.getClient();
    };

    this.getService = function(name) {
        return services[name];
    };
}

/***************************************
/* Communications
 */
var sendInternalMessage = function(msg) {
    chrome.runtime.sendMessage(chrome.runtime.id, msg);
};

var addListener = function(callback) {
    chrome.runtime.onMessage.addListener(callback);
};

/***************************************
/* Service instance that will be running in the background page
 */
var service = new DarkWalletService();

/***************************************
/* Bindings for the page window so we can have easy access
 */

window.connect = function(_server) { return service.connect(_server); };

window.loadIdentity = service.loadIdentity;
window.getIdentity = function(idx) { return service.getIdentity(idx); };
window.getCurrentIdentity = service.getCurrentIdentity;

window.getKeyRing = service.getKeyRing;
window.servicesStatus = service.servicesStatus;
window.getLobbyTransport = service.getLobbyTransport;

window.getClient = service.getClient;
window.getService = service.getService;

window.initAddress = function(_w) {return service.initAddress(_w);};

window.addListener = addListener;
window.sendInternalMessage = sendInternalMessage;
});

<|endoftext|>


<commit_before>'use strict';

module.exports = {
    '.ams.aam.ExConversationChangeNotification': (msg, agent) => {
        msg.type = 'cqm.ExConversationChangeNotification';
        msg.body.changes.forEach(c => {
            delete c.result.effectiveTTR;
            delete c.result.lastUpdateTime;
            delete c.result.numberOfunreadMessages;
            let cd = c.result.conversationDetails;
            delete cd.convId;
            delete cd.dialogs;
            delete cd.note;
            delete cd.brandId;
            // delete cd.firstConversation;
            delete cd.groupId;
            delete cd.csatRate;
            Object.keys(cd.participants).forEach(role =>
                cd.participants[role].filter(id => id === agent.__oldAgentId).forEach(() =>
                    cd.__myRole = role));
            // getMyRole is deprecated. Please use sdk.agentId
            cd.getMyRole = () => cd.__myRole;
            cd.participants = [];
            Object.keys(cd.participantsPId).forEach(role =>
                cd.participantsPId[role].forEach(id =>
                    cd.participants.push({id: id, role: role})));
            delete cd.participantsPId;
        });
        return msg;
    },
    '.ams.routing.SetAgentState': (msg, agent) => {
        msg.body.channels = ['MESSAGING'];
        msg.body.agentUserId = agent.__oldAgentId;
        return msg;
    },
    '.ams.cm.UpdateConversationField': (msg, agent) => {
        msg.body.conversationField.forEach(f => {
            if (f.field === 'ParticipantsChange') {
                f.userId = agent.__oldAgentId;
            }
        });
        return msg;
    },
    '.ams.routing.SubscribeRoutingTasks': (msg, agent) => {
        msg.body.channelType = 'MESSAGING';
        msg.body.agentId = agent.__oldAgentId;
        msg.body.brandId = agent.accountId;
        return msg;
    },
    '.ams.routing.RingUpdated': (msg) => {
        msg.type = 'deprecated';
        delete msg.body; // deprecated notification
        return msg;
    },
    '.ams.aam.SubscribeExConversations': (msg, agent) => {
        msg.body.agentIds = msg.body.agentIds ? msg.body.agentIds
                .map(id => id === agent.agentId ? agent.__oldAgentId : id) : undefined;
        return msg;
    },
    '.ams.routing.RoutingTaskNotification': (msg) => {
        msg.body.changes.forEach(c => {
            delete c.result.routingTaskId;
            delete c.result.assignedAgentId;
            delete c.result.brandId;
            c.result.ringsDetails.forEach(rd => {
                delete rd.brandId;
                delete rd.convId;
                delete rd.consumerId;
                delete rd.skillId;
            });
        });
        msg.type = 'routing.RoutingTaskNotification';
        return msg;
    },
    '.ams.userprofile.GetUserProfile$Response': (msg) => {
        let SDEs = [];
        let authData = msg.body.authenticatedData;
        let privateData = msg.body.privateData || {};
        if (authData && authData.lp_sdes) {
            msg.body.authenticatedData.lp_sdes.forEach(sde => {
                sde.auth = {};
                SDEs.push(sde);
            });
        }
        let ctmrinfo = {
            type: 'ctmrinfo',
            info: {}
        };
        if (privateData.mobileNum) {
            ctmrinfo.info.imei = privateData.mobileNum;
        }
        if (privateData.userId) {
            ctmrinfo.info.customerId = privateData.userId;
        }
        if (Object.keys(ctmrinfo.info).length > 0) {
            SDEs.push(ctmrinfo);
        }

        let personalInfo = {
            type: 'personal',
            personal: {}
        };
        if (msg.body.firstName) {
            personalInfo.personal.firstname = msg.body.firstName;
        }
        if (msg.body.lastName) {
            personalInfo.personal.lastname = msg.body.lastName;
        }
        if (privateData.email || privateData.mobileNum) {
            personalInfo.personal.contacts = [{
                    email: privateData.email,
                    phone: privateData.mobileNum
                }];
        }
        if (Object.keys(personalInfo.personal).length > 0) {
            SDEs.push(personalInfo);
        }

        delete msg.body;
        msg.body = SDEs;
        return msg;
    },
    'ms.SubscribeMessagingEvents': (msg) => {
        // TODO: recored subscribed conversations.
        // agent.subscribed = agent.subscribed || {};
        // agent.subscribed[msg.body.dialogId] = true;
        msg.type = '.ams.ms.QueryMessages';
        msg.body.newerThanSequence = msg.fromSeq || 0;
        return msg;
    },
    '.ams.ms.QueryMessages$Response': (msg, agent) => {
        if (msg.body.length > 0) {
            const dialogId = msg.body[0].dialogId;
            msg.body.forEach(c => {
                c.__isMe = c.originatorId === agent.__oldAgentId;
                // isMe is deprecated. Please use sdk.agentId
                c.isMe = () => c.__isMe;
                c.originatorId = c.originatorPId;
                delete c.originatorPId;
            });
            agent._handleNotification({
                kind: 'notification',
                type: 'ms.MessagingEventNotification',
                body: {dialogId: dialogId, changes: msg.body}
            });
        }
        delete msg.body;
        msg.type = 'GenericSubscribeResponse';
        return msg;
    },
    '.ams.ms.OnlineEventDistribution': (msg, agent) => {
        // TODO: filter non subscribed notifications.
        msg.type = 'ms.MessagingEventNotification';
        msg.body.__isMe = msg.body.originatorId === agent.__oldAgentId;
        msg.body.originatorId = msg.body.originatorPId;
        delete msg.body.originatorPId;
        msg.body = {dialogId: msg.body.dialogId, changes: [msg.body]};
        const c = msg.body.changes[0];
        c.isMe = () => c.__isMe;
        return msg;
    },
    '.ams.cm.AgentRequestConversation': (msg) => {
        msg.body.channelType = 'MESSAGING';
        return msg;
    },
    '.ams.routing.SubscribeAgentsState': (msg, agent) => {
        msg.body.brandId = agent.accountId;
        msg.body.agentId = agent.__oldAgentId;
        return msg;
    },
    '.ams.routing.AgentStateNotification': (msg) => {
        msg.type = 'routing.AgentStateNotification';
        msg.body.changes.forEach(c => {
            delete c.result.agentUserId; // remove old id. Still don't have new uuid.
        });
        return msg;
    }
};
<commit_msg>fixed issue<commit_after>'use strict';

module.exports = {
    '.ams.aam.ExConversationChangeNotification': (msg, agent) => {
        msg.type = 'cqm.ExConversationChangeNotification';
        msg.body.changes.forEach(change => {
            if (change && change.result) {
                delete change.result.effectiveTTR;
                delete change.result.lastUpdateTime;
                delete change.result.numberOfunreadMessages;
                let conversationDetails = change.result.conversationDetails;
                delete conversationDetails.convId;
                delete conversationDetails.dialogs;
                delete conversationDetails.note;
                delete conversationDetails.brandId;
                // delete cd.firstConversation;
                delete conversationDetails.groupId;
                delete conversationDetails.csatRate;
                Object.keys(conversationDetails.participants).forEach(role =>
                    conversationDetails.participants[role].filter(id => id === agent.__oldAgentId).forEach(() =>
                        conversationDetails.__myRole = role));
                // getMyRole is deprecated. Please use sdk.agentId
                conversationDetails.getMyRole = () => conversationDetails.__myRole;
                conversationDetails.participants = [];
                Object.keys(conversationDetails.participantsPId).forEach(role =>
                    conversationDetails.participantsPId[role].forEach(id =>
                        conversationDetails.participants.push({ id: id, role: role })));
                delete conversationDetails.participantsPId;
            }
        });
        return msg;
    },
    '.ams.routing.SetAgentState': (msg, agent) => {
        msg.body.channels = ['MESSAGING'];
        msg.body.agentUserId = agent.__oldAgentId;
        return msg;
    },
    '.ams.cm.UpdateConversationField': (msg, agent) => {
        msg.body.conversationField.forEach(f => {
            if (f.field === 'ParticipantsChange') {
                f.userId = agent.__oldAgentId;
            }
        });
        return msg;
    },
    '.ams.routing.SubscribeRoutingTasks': (msg, agent) => {
        msg.body.channelType = 'MESSAGING';
        msg.body.agentId = agent.__oldAgentId;
        msg.body.brandId = agent.accountId;
        return msg;
    },
    '.ams.routing.RingUpdated': (msg) => {
        msg.type = 'deprecated';
        delete msg.body; // deprecated notification
        return msg;
    },
    '.ams.aam.SubscribeExConversations': (msg, agent) => {
        msg.body.agentIds = msg.body.agentIds ? msg.body.agentIds
            .map(id => id === agent.agentId ? agent.__oldAgentId : id) : undefined;
        return msg;
    },
    '.ams.routing.RoutingTaskNotification': (msg) => {
        msg.body.changes.forEach(c => {
            delete c.result.routingTaskId;
            delete c.result.assignedAgentId;
            delete c.result.brandId;
            c.result.ringsDetails.forEach(rd => {
                delete rd.brandId;
                delete rd.convId;
                delete rd.consumerId;
                delete rd.skillId;
            });
        });
        msg.type = 'routing.RoutingTaskNotification';
        return msg;
    },
    '.ams.userprofile.GetUserProfile$Response': (msg) => {
        let SDEs = [];
        let authData = msg.body.authenticatedData;
        let privateData = msg.body.privateData || {};
        if (authData && authData.lp_sdes) {
            msg.body.authenticatedData.lp_sdes.forEach(sde => {
                sde.auth = {};
                SDEs.push(sde);
            });
        }
        let ctmrinfo = {
            type: 'ctmrinfo',
            info: {}
        };
        if (privateData.mobileNum) {
            ctmrinfo.info.imei = privateData.mobileNum;
        }
        if (privateData.userId) {
            ctmrinfo.info.customerId = privateData.userId;
        }
        if (Object.keys(ctmrinfo.info).length > 0) {
            SDEs.push(ctmrinfo);
        }

        let personalInfo = {
            type: 'personal',
            personal: {}
        };
        if (msg.body.firstName) {
            personalInfo.personal.firstname = msg.body.firstName;
        }
        if (msg.body.lastName) {
            personalInfo.personal.lastname = msg.body.lastName;
        }
        if (privateData.email || privateData.mobileNum) {
            personalInfo.personal.contacts = [{
                email: privateData.email,
                phone: privateData.mobileNum
            }];
        }
        if (Object.keys(personalInfo.personal).length > 0) {
            SDEs.push(personalInfo);
        }

        delete msg.body;
        msg.body = SDEs;
        return msg;
    },
    'ms.SubscribeMessagingEvents': (msg) => {
        // TODO: recored subscribed conversations.
        // agent.subscribed = agent.subscribed || {};
        // agent.subscribed[msg.body.dialogId] = true;
        msg.type = '.ams.ms.QueryMessages';
        msg.body.newerThanSequence = msg.fromSeq || 0;
        return msg;
    },
    '.ams.ms.QueryMessages$Response': (msg, agent) => {
        if (msg.body.length > 0) {
            const dialogId = msg.body[0].dialogId;
            msg.body.forEach(c => {
                c.__isMe = c.originatorId === agent.__oldAgentId;
                // isMe is deprecated. Please use sdk.agentId
                c.isMe = () => c.__isMe;
                c.originatorId = c.originatorPId;
                delete c.originatorPId;
            });
            agent._handleNotification({
                kind: 'notification',
                type: 'ms.MessagingEventNotification',
                body: { dialogId: dialogId, changes: msg.body }
            });
        }
        delete msg.body;
        msg.type = 'GenericSubscribeResponse';
        return msg;
    },
    '.ams.ms.OnlineEventDistribution': (msg, agent) => {
        // TODO: filter non subscribed notifications.
        msg.type = 'ms.MessagingEventNotification';
        msg.body.__isMe = msg.body.originatorId === agent.__oldAgentId;
        msg.body.originatorId = msg.body.originatorPId;
        delete msg.body.originatorPId;
        msg.body = { dialogId: msg.body.dialogId, changes: [msg.body] };
        const c = msg.body.changes[0];
        c.isMe = () => c.__isMe;
        return msg;
    },
    '.ams.cm.AgentRequestConversation': (msg) => {
        msg.body.channelType = 'MESSAGING';
        return msg;
    },
    '.ams.routing.SubscribeAgentsState': (msg, agent) => {
        msg.body.brandId = agent.accountId;
        msg.body.agentId = agent.__oldAgentId;
        return msg;
    },
    '.ams.routing.AgentStateNotification': (msg) => {
        msg.type = 'routing.AgentStateNotification';
        msg.body.changes.forEach(c => {
            delete c.result.agentUserId; // remove old id. Still don't have new uuid.
        });
        return msg;
    }
};
<|endoftext|>