<commit_before>var types = require("../lib/HAP-NodeJS/accessories/types.js");
var request = require('request');
var net = require('net')
/* TODO:
 * - send keepalive
 * - auto reconnect on disconnect
 * - wait for success after command and then send the next
 * - support more devices
 */

PilightDeviceTypes = {
  RAW: 0,
  SWITCH: 1,
  DIMMER: 2,
  WEATHER: 3,
  RELAY: 4,
  SCREEN: 5,
  CONTACT: 6,
  PENDINGSW: 7,
  DATETIME: 8,
  XBMC: 9,
  LIRC: 10,
  WEBCAM: 11
}

function PilightPlatform(log, config){
  this.name = config['name']
  this.host = config['host']
  this.port = config['port']
  this.protocol = config['protocol']
  this.http_port = config['http-port']

  this.log = log;
}

function PilightClient(log, platform) {
    var that= this;
    var bootstrap = function() {
        log('connected to server!');
        var cmd = {
          "action": "identify",
          "options": {
            "config": 1
          }
        };
        that.send(cmd);
    }

    this.log = log;
    this.registry = [];
    this.socket = net.connect({host:platform.host, port:platform.port}, bootstrap);

    this.socket.on('data', function(data) {
      try {
        data = JSON.parse(data);
      } catch(e) {
        if (e instanceof SyntaxError) { that.log("SyntaxError in " + data.toString()); }
        else { that.log("Error " + JSON.stringify(e)); }
      }

      if(data['origin'] !== 'update') {
        return
      }
      that.updateDevices(data['devices'], data['values']);
    });

    this.socket.on('end', function() {
      that.log('disconnected from server');
    });
}
PilightClient.prototype = {
  send: function(cmd) {
    var msg = JSON.stringify(cmd);
    this.log("[PilightClient.send] "+msg);
    this.socket.write(msg);
  },

  register: function(idx, accessory) {
    this.registry[idx] = accessory;
  },

  updateDevices: function(devices, values) {
    for(var device of devices) {
      var dev = this.registry[device]
      if(dev === undefined || dev.length === 0) {
        continue
      }
      this.log("[PilightClient.updateDevices] External update on device "
               + device);
      dev.updateCharacteristics(values);
    }
  }
}

PilightPlatform.prototype = {
  accessories: function(callback) {
    // XXX request config over socket crashes pilight
    var url = this.protocol + '://' + this.host + ':'
                + this.http_port +'/config';

    this.log("[PilightPlatform.accessories] Fetching pilight devices.");
    this.log("[PilightPlatform.accessories] Fetching from: "+url);

    var collected = [];
    var that = this;
    var client = new PilightClient(this.log, this);

    request.get({url: url, json:true}, function(err, response, json) {
      var accessory = null;

      if (!err && response.statusCode == 200) {

        for (var device_name in json['gui']) {

          var gui = json['gui'][device_name]
          var device = json['devices'][device_name]
          var accessory;

          for (var proto of device['protocol']) {

            that.log("[PilightPlatform.accessories] " + gui['name'] + " : " + proto);

            switch(proto) {
              case "elro_800_switch":
                accessory = PilightSwitch;
                break;
              case 'openweathermap':
                accessory = PilightThermostat;
                break;
              default: // arping, sunriseset, datetime, generic_label
                that.log("Unsupported pilight device (" + gui['name']
                         + ") with protocol " + device['protocol']);
                continue;
            }
            that.log("Adding pilight device (" + gui['name'] + ") with protocol "
                     + device['protocol']);
            collected.push(
                    new accessory(that.log, device_name, gui['name'], proto, device, client));
            break;  // we found a supported protocol, so we are done with this device
          }
        }
        callback(collected);
      } else {
        that.log("There was a problem requesting the configuration from pilight.");
      }
    });
  }
}

function PilightSwitch(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

PilightSwitch.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightSwitch.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    var newValue = (values['state'] === 'on')*1;
    for(var c of this.registry) {
      if(c.value === newValue) {
        this.log("[PilightSwitch.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightSwitch.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
      c.updateValue(newValue);
    }
  },

  setPowerState: function(powerOn) {
    var that = this;
    this.log("[PilightSwitch.setPowerState] "+powerOn);
    this.client.send({
          "action": "control",
          "code": {
            "device": that.idx,
            "state": (powerOn)?"on":"off"
          }});
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of the accessory",
        designedMaxLength: 255
      },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "pilight",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Manufacturer",
        designedMaxLength: 255
      },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Model",
        designedMaxLength: 255
      },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "BIVAB-PO-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "SN",
        designedMaxLength: 255
      },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
    },{
      sType: types.OUTLET_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + " outlet service",
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Name of service",
        designedMaxLength: 255
      },{
        cType: types.POWER_STATE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
        perms: ["pw","pr","ev"],
        format: "bool",
        initialValue: (this.state==='on')?1:0,
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Change the power state of the outlet",
        designedMaxLength: 1
      },{
        cType: types.OUTLET_IN_USE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        perms: ["pr","ev"],
        format: "bool",
        initialValue: true,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Turn On the Light",
        designedMaxLength: 1
      }]
    }];
  }
};

// XXX refactory and merge with Switch
function PilightThermostat(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

// XXX merge with Switch prototype and extract common prototype
PilightThermostat.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightThermostat.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    this.log(values);
    var newValue;
    for(var c of this.registry) {
      switch(c.type) {
        case types.CURRENT_TEMPERATURE_CTYPE:
          newValue = values['temperature'];
          break;
        case types.CURRENT_RELATIVE_HUMIDITY_CTYPE:
          newValue = values['humidity'];
          break;
      }
      if(newValue === undefined || c.value === newValue) {
        this.log("[PilightThermostat.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightThermostat.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
    }
    c.updateValue(newValue);
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + "Thermostat 1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Oltica",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "A1S2NASF88EW",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
      },{
      sType: types.THERMOSTAT_STYPE,
      characteristics: [{
          cType: types.NAME_CTYPE,
          onUpdate: null,
          perms: ["pr"],
          format: "string",
          initialValue: that.name + "Thermostat Control",
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Bla",
          designedMaxLength: 255
        },{
          cType: types.CURRENTHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Mode",
          designedMaxLength: 1,
          designedMinValue: 0,
          designedMaxValue: 2,
          designedMinStep: 1,
        },{
          cType: types.TARGETHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Mode",
          designedMinValue: 0,
          designedMaxValue: 3,
          designedMinStep: 1,
        },{
          cType: types.CURRENT_TEMPERATURE_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Temperature",
          unit: "celsius"
        },{
          cType: types.TARGET_TEMPERATURE_CTYPE,
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Temperature",
          designedMinValue: 16,
          designedMaxValue: 38,
          designedMinStep: 1,
          unit: "celsius"
        },{
          cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "float",
          initialValue: parseFloat(that.info['humidity']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current relative humidity",
          unit: "percentage",
          designedMinValue: 0,
          designedMaxValue: 100,
          designedMinStep: 1
        },{
          cType: types.TEMPERATURE_UNITS_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Unit",
        }]
      }];
  }
};
module.exports.accessory = [PilightSwitch, PilightThermostat];
module.exports.platform = PilightPlatform;
<commit_msg>Added heartbeat and reconnection<commit_after>var types = require("../lib/HAP-NodeJS/accessories/types.js");
var request = require('request');
var net = require('net')
/* TODO:
 * - send keepalive
 * - auto reconnect on disconnect
 * - wait for success after command and then send the next
 * - support more devices
 */

var DEBUG = true;

PilightDeviceTypes = {
  RAW: 0,
  SWITCH: 1,
  DIMMER: 2,
  WEATHER: 3,
  RELAY: 4,
  SCREEN: 5,
  CONTACT: 6,
  PENDINGSW: 7,
  DATETIME: 8,
  XBMC: 9,
  LIRC: 10,
  WEBCAM: 11
}

function PilightPlatform(log, config){
  this.name = config['name']
  this.host = config['host']
  this.port = config['port']
  this.protocol = config['protocol']
  this.http_port = config['http-port']

  this.log = log;
}

function PilightClient(log, platform) {
    var that= this;

    this.log = log;
    this.platform = platform;
    this.registry = [];

    this.connect();
    this.startHeartBeat();
}

PilightClient.prototype = {
  send: function(cmd) {
    var msg = JSON.stringify(cmd);
    this.log("[PilightClient.send] "+msg);
    this.socket.write(msg);
  },

  register: function(idx, accessory) {
    this.registry[idx] = accessory;
  },

  updateDevices: function(devices, values) {
    for(var device of devices) {
      var dev = this.registry[device]
      if(dev === undefined || dev.length === 0) {
        continue
      }
      this.log("[PilightClient.updateDevices] External update on device "
               + device);
      dev.updateCharacteristics(values);
    }
  },

  startHeartBeat: function() {
    var that = this;
    var cb = function() { that.socket.write("HEART\n", "utf-8"); };
    this.heartBeatInterval = setInterval(cb, 1000);
  },

  onBeat: function() {
    if(DEBUG) { this.log("BEAT");}

    if(this.heartBeatTimeout) {
      clearTimeout(this.heartBeatTimeout);
    }

    var that = this;
    var reconnectDelay = 10000;
    var reconnect = function() {
      that.log('[PilightClient.reconnect] Trying reconnect');
      try{
        that.socket.end();
        that.socket.destroy();
      }
      finally {
        that.connect();
      }
    }
    this.heartBeatTimeout = setTimeout(reconnect, reconnectDelay);
  },

  connect: function() {
    var that = this;
    var bootstrap = function() {
      that.log('connected to server!');
      var cmd = {
        "action": "identify",
        "options": {
          "config": 1
        }
      };
      that.send(cmd);
    }

    this.socket = net.connect({host:this.platform.host, port:this.platform.port}, bootstrap);

    this.socket.on('data', function(data) {
      data = data.toString();
      for(msg of data.split('\n')) {
        if(msg.length === 0) { continue; }

        if(msg === "BEAT") {
          that.onBeat();
          continue;
        }

        try {
          data = JSON.parse(data);
        } catch(e) {
          if (e instanceof SyntaxError) { that.log("SyntaxError in " + msg); }
          else { that.log("Error " + JSON.stringify(e)); }
          continue;
        }
        if(data['origin'] !== 'update') {
          continue;
        }
        that.updateDevices(data['devices'], data['values']);
      }
    });

    this.socket.on('end', function() {
      clearTimeout(that.heartBeatTimeout);
      clearTimeout(that.heartBeatInterval);
      that.log('disconnected from server');
    });
  }
}

PilightPlatform.prototype = {
  accessories: function(callback) {
    // XXX request config over socket crashes pilight
    var url = this.protocol + '://' + this.host + ':'
                + this.http_port +'/config';

    this.log("[PilightPlatform.accessories] Fetching pilight devices.");
    this.log("[PilightPlatform.accessories] Fetching from: "+url);

    var collected = [];
    var that = this;
    var client = new PilightClient(this.log, this);

    request.get({url: url, json:true}, function(err, response, json) {
      var accessory = null;

      if (!err && response.statusCode == 200) {

        for (var device_name in json['gui']) {

          var gui = json['gui'][device_name]
          var device = json['devices'][device_name]
          var accessory;

          for (var proto of device['protocol']) {

            that.log("[PilightPlatform.accessories] " + gui['name'] + " : " + proto);

            switch(proto) {
              case "elro_800_switch":
                accessory = PilightSwitch;
                break;
              case 'openweathermap':
                accessory = PilightThermostat;
                break;
              default: // arping, sunriseset, datetime, generic_label
                that.log("Unsupported pilight device (" + gui['name']
                         + ") with protocol " + device['protocol']);
                continue;
            }
            that.log("Adding pilight device (" + gui['name'] + ") with protocol "
                     + device['protocol']);
            collected.push(
                    new accessory(that.log, device_name, gui['name'], proto, device, client));
            break;  // we found a supported protocol, so we are done with this device
          }
        }
        callback(collected);
      } else {
        that.log("There was a problem requesting the configuration from pilight.");
      }
    });
  }
}

function PilightSwitch(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

PilightSwitch.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightSwitch.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    var newValue = (values['state'] === 'on')*1;
    for(var c of this.registry) {
      if(c.value === newValue) {
        this.log("[PilightSwitch.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightSwitch.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
      c.updateValue(newValue);
    }
  },

  setPowerState: function(powerOn) {
    var that = this;
    this.log("[PilightSwitch.setPowerState] "+powerOn);
    this.client.send({
          "action": "control",
          "code": {
            "device": that.idx,
            "state": (powerOn)?"on":"off"
          }});
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Name of the accessory",
        designedMaxLength: 255
      },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "pilight",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Manufacturer",
        designedMaxLength: 255
      },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Model",
        designedMaxLength: 255
      },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "BIVAB-PO-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "SN",
        designedMaxLength: 255
      },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
    },{
      sType: types.OUTLET_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + " outlet service",
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Name of service",
        designedMaxLength: 255
      },{
        cType: types.POWER_STATE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
        perms: ["pw","pr","ev"],
        format: "bool",
        initialValue: (this.state==='on')?1:0,
        supportEvents: true,
        supportBonjour: false,
        manfDescription: "Change the power state of the outlet",
        designedMaxLength: 1
      },{
        cType: types.OUTLET_IN_USE_CTYPE,
        onUpdate: function(value) { that.setPowerState(value)},
        perms: ["pr","ev"],
        format: "bool",
        initialValue: true,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Turn On the Light",
        designedMaxLength: 1
      }]
    }];
  }
};

// XXX refactory and merge with Switch
function PilightThermostat(log, idx, name, protocol, info, client) {
  // device info
  log("Name: "+idx);
  this.idx = idx;
  this.name = name;
  this.log = log;
  this.client = client;
  this.registry = [];
  this.info = info;
  this.protocol = protocol;
}

// XXX merge with Switch prototype and extract common prototype
PilightThermostat.prototype = {

  registerForUpdates: function(characteristic) {
    this.log("[PilightThermostat.registerForUpdates] Registering device "+this.idx
             + " for updates on characteristic '"+characteristic.manfDescription+"'");

    this.registry.push(characteristic);
    this.client.register(this.idx, this);
  },

  updateCharacteristics: function(values) {
    this.log(values);
    var newValue;
    for(var c of this.registry) {
      switch(c.type) {
        case types.CURRENT_TEMPERATURE_CTYPE:
          newValue = values['temperature'];
          break;
        case types.CURRENT_RELATIVE_HUMIDITY_CTYPE:
          newValue = values['humidity'];
          break;
      }
      if(newValue === undefined || c.value === newValue) {
        this.log("[PilightThermostat.updateCharacteristics] No state change "
                 + "detected on characteristic, skiping event");
        continue;
      }
      this.log("[PilightThermostat.updateCharacteristics] Updating device "
               +this.name + " to value "+newValue+" on characteristic '"
               +JSON.stringify(c.manfDescription)+"'");
    }
    c.updateValue(newValue);
  },

  getServices: function() {
    var that = this;
    return [{
      sType: types.ACCESSORY_INFORMATION_STYPE,
      characteristics: [{
        cType: types.NAME_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: that.name + "Thermostat 1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MANUFACTURER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Oltica",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.MODEL_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "Rev-1",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.SERIAL_NUMBER_CTYPE,
        onUpdate: null,
        perms: ["pr"],
        format: "string",
        initialValue: "A1S2NASF88EW",
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Bla",
        designedMaxLength: 255
        },{
        cType: types.IDENTIFY_CTYPE,
        onUpdate: null,
        perms: ["pw"],
        format: "bool",
        initialValue: false,
        supportEvents: false,
        supportBonjour: false,
        manfDescription: "Identify Accessory",
        designedMaxLength: 1
      }]
      },{
      sType: types.THERMOSTAT_STYPE,
      characteristics: [{
          cType: types.NAME_CTYPE,
          onUpdate: null,
          perms: ["pr"],
          format: "string",
          initialValue: that.name + "Thermostat Control",
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Bla",
          designedMaxLength: 255
        },{
          cType: types.CURRENTHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Mode",
          designedMaxLength: 1,
          designedMinValue: 0,
          designedMaxValue: 2,
          designedMinStep: 1,
        },{
          cType: types.TARGETHEATINGCOOLING_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Mode",
          designedMinValue: 0,
          designedMaxValue: 3,
          designedMinStep: 1,
        },{
          cType: types.CURRENT_TEMPERATURE_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current Temperature",
          unit: "celsius"
        },{
          cType: types.TARGET_TEMPERATURE_CTYPE,
          onUpdate: null,
          perms: ["pr","ev"],
          format: "int",
          initialValue: parseFloat(that.info['temperature']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Target Temperature",
          designedMinValue: 16,
          designedMaxValue: 38,
          designedMinStep: 1,
          unit: "celsius"
        },{
          cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE,
          onRegister: function(characteristic) { that.registerForUpdates(characteristic) },
          onUpdate: null,
          perms: ["pr","ev"],
          format: "float",
          initialValue: parseFloat(that.info['humidity']),
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Current relative humidity",
          unit: "percentage",
          designedMinValue: 0,
          designedMaxValue: 100,
          designedMinStep: 1
        },{
          cType: types.TEMPERATURE_UNITS_CTYPE,
          perms: ["pr","ev"],
          format: "int",
          initialValue: 0,
          supportEvents: false,
          supportBonjour: false,
          manfDescription: "Unit",
        }]
      }];
  }
};
module.exports.accessory = [PilightSwitch, PilightThermostat];
module.exports.platform = PilightPlatform;
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2007 Apple Inc.  All rights reserved.
 * Copyright (C) 2009 Joseph Pecoraro
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @implements {UI.ToolbarItem.ItemsProvider}
 * @unrestricted
 */
Elements.EventListenersWidget = class extends UI.ThrottledWidget {
  constructor() {
    super();
    this._toolbarItems = [];

    this._showForAncestorsSetting = Common.settings.moduleSetting('showEventListenersForAncestors');
    this._showForAncestorsSetting.addChangeListener(this.update.bind(this));

    this._dispatchFilterBySetting = Common.settings.createSetting(
        'eventListenerDispatchFilterType', Elements.EventListenersWidget.DispatchFilterBy.All);
    this._dispatchFilterBySetting.addChangeListener(this.update.bind(this));

    this._showFrameworkListenersSetting = Common.settings.createSetting('showFrameowkrListeners', true);
    this._showFrameworkListenersSetting.setTitle(Common.UIString('Framework listeners'));
    this._showFrameworkListenersSetting.addChangeListener(this._showFrameworkListenersChanged.bind(this));
    this._eventListenersView = new EventListeners.EventListenersView(this.update.bind(this));
    this._eventListenersView.show(this.element);

    const refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
    refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this.update.bind(this));
    this._toolbarItems.push(refreshButton);
    this._toolbarItems.push(new UI.ToolbarSettingCheckbox(
        this._showForAncestorsSetting, Common.UIString('Show listeners on the ancestors'),
        Common.UIString('Ancestors')));
    const dispatchFilter = new UI.ToolbarComboBox(this._onDispatchFilterTypeChanged.bind(this));

    /**
     * @param {string} name
     * @param {string} value
     * @this {Elements.EventListenersWidget}
     */
    function addDispatchFilterOption(name, value) {
      const option = dispatchFilter.createOption(name, '', value);
      if (value === this._dispatchFilterBySetting.get())
        dispatchFilter.select(option);
    }
    addDispatchFilterOption.call(this, Common.UIString('All'), Elements.EventListenersWidget.DispatchFilterBy.All);
    addDispatchFilterOption.call(
        this, Common.UIString('Passive'), Elements.EventListenersWidget.DispatchFilterBy.Passive);
    addDispatchFilterOption.call(
        this, Common.UIString('Blocking'), Elements.EventListenersWidget.DispatchFilterBy.Blocking);
    dispatchFilter.setMaxWidth(200);
    this._toolbarItems.push(dispatchFilter);
    this._toolbarItems.push(new UI.ToolbarSettingCheckbox(
        this._showFrameworkListenersSetting, Common.UIString('Resolve event listeners bound with framework')));

    UI.context.addFlavorChangeListener(SDK.DOMNode, this.update, this);
    this.update();
  }

  /**
   * @override
   * @protected
   * @return {!Promise.<?>}
   */
  doUpdate() {
    if (this._lastRequestedNode) {
      this._lastRequestedNode.domModel().runtimeModel().releaseObjectGroup(
          Elements.EventListenersWidget._objectGroupName);
      delete this._lastRequestedNode;
    }
    const node = UI.context.flavor(SDK.DOMNode);
    if (!node) {
      this._eventListenersView.reset();
      this._eventListenersView.addEmptyHolderIfNeeded();
      return Promise.resolve();
    }
    this._lastRequestedNode = node;
    const selectedNodeOnly = !this._showForAncestorsSetting.get();
    const promises = [];
    promises.push(node.resolveToObject(Elements.EventListenersWidget._objectGroupName));
    if (!selectedNodeOnly) {
      let currentNode = node.parentNode;
      while (currentNode) {
        promises.push(currentNode.resolveToObject(Elements.EventListenersWidget._objectGroupName));
        currentNode = currentNode.parentNode;
      }
      promises.push(this._windowObjectInNodeContext(node));
    }
    return Promise.all(promises)
        .then(this._eventListenersView.addObjects.bind(this._eventListenersView))
        .then(this._showFrameworkListenersChanged.bind(this));
  }

  /**
   * @override
   * @return {!Array<!UI.ToolbarItem>}
   */
  toolbarItems() {
    return this._toolbarItems;
  }

  /**
   * @param {!Event} event
   */
  _onDispatchFilterTypeChanged(event) {
    this._dispatchFilterBySetting.set(event.target.value);
  }

  _showFrameworkListenersChanged() {
    const dispatchFilter = this._dispatchFilterBySetting.get();
    const showPassive = dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.All ||
        dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.Passive;
    const showBlocking = dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.All ||
        dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.Blocking;
    this._eventListenersView.showFrameworkListeners(
        this._showFrameworkListenersSetting.get(), showPassive, showBlocking);
  }

  /**
   * @param {!SDK.DOMNode} node
   * @return {!Promise<?SDK.RemoteObject>}
   */
  _windowObjectInNodeContext(node) {
    const executionContexts = node.domModel().runtimeModel().executionContexts();
    let context = null;
    if (node.frameId()) {
      for (let i = 0; i < executionContexts.length; ++i) {
        const executionContext = executionContexts[i];
        if (executionContext.frameId === node.frameId() && executionContext.isDefault)
          context = executionContext;
      }
    } else {
      context = executionContexts[0];
    }
    return context
        .evaluate(
            {
              expression: 'self',
              objectGroup: Elements.EventListenersWidget._objectGroupName,
              includeCommandLineAPI: false,
              silent: true,
              returnByValue: false,
              generatePreview: false
            },
            /* userGesture */ false,
            /* awaitPromise */ false)
        .then(result => result.object || null);
  }

  _eventListenersArrivedForTest() {
  }
};

Elements.EventListenersWidget.DispatchFilterBy = {
  All: 'All',
  Blocking: 'Blocking',
  Passive: 'Passive'
};

Elements.EventListenersWidget._objectGroupName = 'event-listeners-panel';
<commit_msg>DevTools: aXe Core linting for Elements - Event Listeners widget.<commit_after>/*
 * Copyright (C) 2007 Apple Inc.  All rights reserved.
 * Copyright (C) 2009 Joseph Pecoraro
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @implements {UI.ToolbarItem.ItemsProvider}
 * @unrestricted
 */
Elements.EventListenersWidget = class extends UI.ThrottledWidget {
  constructor() {
    super();
    this._toolbarItems = [];

    this._showForAncestorsSetting = Common.settings.moduleSetting('showEventListenersForAncestors');
    this._showForAncestorsSetting.addChangeListener(this.update.bind(this));

    this._dispatchFilterBySetting = Common.settings.createSetting(
        'eventListenerDispatchFilterType', Elements.EventListenersWidget.DispatchFilterBy.All);
    this._dispatchFilterBySetting.addChangeListener(this.update.bind(this));

    this._showFrameworkListenersSetting = Common.settings.createSetting('showFrameowkrListeners', true);
    this._showFrameworkListenersSetting.setTitle(Common.UIString('Framework listeners'));
    this._showFrameworkListenersSetting.addChangeListener(this._showFrameworkListenersChanged.bind(this));
    this._eventListenersView = new EventListeners.EventListenersView(this.update.bind(this));
    this._eventListenersView.show(this.element);

    const refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
    refreshButton.addEventListener(UI.ToolbarButton.Events.Click, this.update.bind(this));
    this._toolbarItems.push(refreshButton);
    this._toolbarItems.push(new UI.ToolbarSettingCheckbox(
        this._showForAncestorsSetting, Common.UIString('Show listeners on the ancestors'),
        Common.UIString('Ancestors')));
    const dispatchFilter = new UI.ToolbarComboBox(this._onDispatchFilterTypeChanged.bind(this));
    dispatchFilter.setTitle(ls`Event listeners category`);

    /**
     * @param {string} name
     * @param {string} value
     * @this {Elements.EventListenersWidget}
     */
    function addDispatchFilterOption(name, value) {
      const option = dispatchFilter.createOption(name, '', value);
      if (value === this._dispatchFilterBySetting.get())
        dispatchFilter.select(option);
    }
    addDispatchFilterOption.call(this, Common.UIString('All'), Elements.EventListenersWidget.DispatchFilterBy.All);
    addDispatchFilterOption.call(
        this, Common.UIString('Passive'), Elements.EventListenersWidget.DispatchFilterBy.Passive);
    addDispatchFilterOption.call(
        this, Common.UIString('Blocking'), Elements.EventListenersWidget.DispatchFilterBy.Blocking);
    dispatchFilter.setMaxWidth(200);
    this._toolbarItems.push(dispatchFilter);
    this._toolbarItems.push(new UI.ToolbarSettingCheckbox(
        this._showFrameworkListenersSetting, Common.UIString('Resolve event listeners bound with framework')));

    UI.context.addFlavorChangeListener(SDK.DOMNode, this.update, this);
    this.update();
  }

  /**
   * @override
   * @protected
   * @return {!Promise.<?>}
   */
  doUpdate() {
    if (this._lastRequestedNode) {
      this._lastRequestedNode.domModel().runtimeModel().releaseObjectGroup(
          Elements.EventListenersWidget._objectGroupName);
      delete this._lastRequestedNode;
    }
    const node = UI.context.flavor(SDK.DOMNode);
    if (!node) {
      this._eventListenersView.reset();
      this._eventListenersView.addEmptyHolderIfNeeded();
      return Promise.resolve();
    }
    this._lastRequestedNode = node;
    const selectedNodeOnly = !this._showForAncestorsSetting.get();
    const promises = [];
    promises.push(node.resolveToObject(Elements.EventListenersWidget._objectGroupName));
    if (!selectedNodeOnly) {
      let currentNode = node.parentNode;
      while (currentNode) {
        promises.push(currentNode.resolveToObject(Elements.EventListenersWidget._objectGroupName));
        currentNode = currentNode.parentNode;
      }
      promises.push(this._windowObjectInNodeContext(node));
    }
    return Promise.all(promises)
        .then(this._eventListenersView.addObjects.bind(this._eventListenersView))
        .then(this._showFrameworkListenersChanged.bind(this));
  }

  /**
   * @override
   * @return {!Array<!UI.ToolbarItem>}
   */
  toolbarItems() {
    return this._toolbarItems;
  }

  /**
   * @param {!Event} event
   */
  _onDispatchFilterTypeChanged(event) {
    this._dispatchFilterBySetting.set(event.target.value);
  }

  _showFrameworkListenersChanged() {
    const dispatchFilter = this._dispatchFilterBySetting.get();
    const showPassive = dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.All ||
        dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.Passive;
    const showBlocking = dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.All ||
        dispatchFilter === Elements.EventListenersWidget.DispatchFilterBy.Blocking;
    this._eventListenersView.showFrameworkListeners(
        this._showFrameworkListenersSetting.get(), showPassive, showBlocking);
  }

  /**
   * @param {!SDK.DOMNode} node
   * @return {!Promise<?SDK.RemoteObject>}
   */
  _windowObjectInNodeContext(node) {
    const executionContexts = node.domModel().runtimeModel().executionContexts();
    let context = null;
    if (node.frameId()) {
      for (let i = 0; i < executionContexts.length; ++i) {
        const executionContext = executionContexts[i];
        if (executionContext.frameId === node.frameId() && executionContext.isDefault)
          context = executionContext;
      }
    } else {
      context = executionContexts[0];
    }
    return context
        .evaluate(
            {
              expression: 'self',
              objectGroup: Elements.EventListenersWidget._objectGroupName,
              includeCommandLineAPI: false,
              silent: true,
              returnByValue: false,
              generatePreview: false
            },
            /* userGesture */ false,
            /* awaitPromise */ false)
        .then(result => result.object || null);
  }

  _eventListenersArrivedForTest() {
  }
};

Elements.EventListenersWidget.DispatchFilterBy = {
  All: 'All',
  Blocking: 'Blocking',
  Passive: 'Passive'
};

Elements.EventListenersWidget._objectGroupName = 'event-listeners-panel';
<|endoftext|>


<commit_before>export const USI_BLACK_PIECES = ['P', 'L', 'N', 'S', 'G', 'K', 'R', 'B'];
export const USI_WHITE_PIECES = ['p', 'l', 'n', 's', 'g', 'k', 'r', 'b'];

export const USI_BLACK_TEAM = 'black';
export const USI_WHITE_TEAM = 'white';

export const USI_PAWN_BLACK_TYPE = 'P';
export const USI_PAWN_BLACK_PROMOTE_TYPE = 'P+';
export const USI_PAWN_WHITE_TYPE = 'p';
export const USI_PAWN_WHITE_PROMOTE_TYPE = 'p+';

export const USI_LANCE_BLACK_TYPE = 'L';
export const USI_LANCE_BLACK_PROMOTE_TYPE = 'L+';
export const USI_LANCE_WHITE_TYPE = 'l';
export const USI_LANCE_WHITE_PROMOTE_TYPE = 'l+';

export const USI_KNIGHT_BLACK_TYPE = 'N';
export const USI_KNIGHT_BLACK_PROMOTE_TYPE = 'N+';
export const USI_KNIGHT_WHITE_TYPE = 'n';
export const USI_KNIGHT_WHITE_PROMOTE_TYPE = 'n+';

export const USI_SILVER_BLACK_TYPE = 'S';
export const USI_SILVER_BLACK_PROMOTE_TYPE = 'S+';
export const USI_SILVER_WHITE_TYPE = 's';
export const USI_SILVER_WHITE_PROMOTE_TYPE = 's+';

export const USI_GOLD_BLACK_TYPE = 'G';
export const USI_GOLD_WHITE_TYPE = 'g';

export const USI_KING_BLACK_TYPE = 'K';
export const USI_KING_WHITE_TYPE = 'k';
<commit_msg>Add Constants of Rook.<commit_after>export const USI_BLACK_PIECES = ['P', 'L', 'N', 'S', 'G', 'K', 'R', 'B'];
export const USI_WHITE_PIECES = ['p', 'l', 'n', 's', 'g', 'k', 'r', 'b'];

export const USI_BLACK_TEAM = 'black';
export const USI_WHITE_TEAM = 'white';

export const USI_PAWN_BLACK_TYPE = 'P';
export const USI_PAWN_BLACK_PROMOTE_TYPE = 'P+';
export const USI_PAWN_WHITE_TYPE = 'p';
export const USI_PAWN_WHITE_PROMOTE_TYPE = 'p+';

export const USI_LANCE_BLACK_TYPE = 'L';
export const USI_LANCE_BLACK_PROMOTE_TYPE = 'L+';
export const USI_LANCE_WHITE_TYPE = 'l';
export const USI_LANCE_WHITE_PROMOTE_TYPE = 'l+';

export const USI_KNIGHT_BLACK_TYPE = 'N';
export const USI_KNIGHT_BLACK_PROMOTE_TYPE = 'N+';
export const USI_KNIGHT_WHITE_TYPE = 'n';
export const USI_KNIGHT_WHITE_PROMOTE_TYPE = 'n+';

export const USI_SILVER_BLACK_TYPE = 'S';
export const USI_SILVER_BLACK_PROMOTE_TYPE = 'S+';
export const USI_SILVER_WHITE_TYPE = 's';
export const USI_SILVER_WHITE_PROMOTE_TYPE = 's+';

export const USI_GOLD_BLACK_TYPE = 'G';
export const USI_GOLD_WHITE_TYPE = 'g';

export const USI_ROOK_BLACK_TYPE = 'R';
export const USI_ROOK_BLACK_PROMOTE_TYPE = 'R+';
export const USI_ROOK_WHITE_TYPE = 'r';
export const USI_ROOK_WHITE_PROMOTE_TYPE = 'r+';

export const USI_KING_BLACK_TYPE = 'K';
export const USI_KING_WHITE_TYPE = 'k';
<|endoftext|>


<commit_before>if (typeof exports != "object" || typeof module != "object")
  throw new Error("This plugin works only in a CommonJS environment")

var infer = require("../lib/infer");
var tern = require("../lib/tern");
require("./commonjs");
require("./es_modules");

var fs = require('fs');
var path = require("path");
var ResolverFactory = require("enhanced-resolve").ResolverFactory;
var SyncNodeJsInputFileSystem = require("enhanced-resolve/lib/SyncNodeJsInputFileSystem");

function getResolver(modules, configPath) {
  var config = {
    unsafeCache: true,
    modules: modules || ["node_modules"],
    extensions: [".js", ".jsx", ".json"],
    aliasFields: ["browser"],
    mainFields: ["browser", "web", "browserify", "main"],
    fileSystem: new SyncNodeJsInputFileSystem()
  }
  var webpackConfig = (configPath && fs.existsSync(configPath)) ? require(configPath) : null
  if (typeof webpackConfig === 'function') {
    webpackConfig = webpackConfig()
  }
  var resolveConfig = webpackConfig && webpackConfig.resolve
  if (resolveConfig) {
    Object.keys(resolveConfig).forEach(function (key) {
      if (key === 'packageMains') {
        config.mainFields = resolveConfig[key]
      } else if (key === 'root') {
        config.modules.unshift(resolveConfig[key])
      } else if (key === 'modules') {
        config.modules = config.modules.concat(resolveConfig[key])
      } else {
        config[key] = resolveConfig[key]
      }
    })
  }

  return ResolverFactory.createResolver(config);
}

function resolveToFile(resolver, name, parentFile) {
    var projectDir = infer.cx().parent.projectDir;
    var fullParent = path.resolve(projectDir, parentFile);
    try {
      return resolver.resolveSync({}, path.dirname(fullParent), name);
    } catch(e) {
      console.log(e.stack)
      return ''
    }
}

tern.registerPlugin("webpack", function(server, options) {
  var configPath = options.configPath || './webpack.config.js'
  var modules = options.modules || ['node_modules']
  configPath = path.resolve(server.options.projectDir, configPath)
  var resolver = getResolver(modules, configPath)
  server.loadPlugin("commonjs")
  server.loadPlugin("es_modules")
  server.mod.modules.resolvers.push(function (name, parentFile) {
    var resolved = resolveToFile(resolver, name, parentFile)
    return resolved && infer.cx().parent.normalizeFilename(resolved)
  })
})
<commit_msg>[webpack plugin] Fix root array support<commit_after>if (typeof exports != "object" || typeof module != "object")
  throw new Error("This plugin works only in a CommonJS environment")

var infer = require("../lib/infer");
var tern = require("../lib/tern");
require("./commonjs");
require("./es_modules");

function isArray(v) {
  return Object.prototype.toString.call(v) == "[object Array]";
};

var fs = require('fs');
var path = require("path");
var ResolverFactory = require("enhanced-resolve").ResolverFactory;
var SyncNodeJsInputFileSystem = require("enhanced-resolve/lib/SyncNodeJsInputFileSystem");

function getResolver(modules, configPath) {
  var config = {
    unsafeCache: true,
    modules: modules || ["node_modules"],
    extensions: [".js", ".jsx", ".json"],
    aliasFields: ["browser"],
    mainFields: ["browser", "web", "browserify", "main"],
    fileSystem: new SyncNodeJsInputFileSystem()
  }
  var webpackConfig = (configPath && fs.existsSync(configPath)) ? require(configPath) : null
  if (typeof webpackConfig === 'function') {
    webpackConfig = webpackConfig()
  }
  var resolveConfig = webpackConfig && webpackConfig.resolve
  if (resolveConfig) {
    Object.keys(resolveConfig).forEach(function (key) {
      if (key === 'packageMains') {
        config.mainFields = resolveConfig[key]
      } else if (key === 'root') {
        var roots = resolveConfig[key]
        if (isArray(roots)) {
          config.modules = roots.concat(config.modules)
        } else {
          config.modules.unshift(roots)
        }
      } else if (key === 'fallback') {
        var fallback = resolveConfig[key]
        if (isArray(fallback)) {
          config.modules = config.modules.concat(fallback)
        } else {
          config.modules.push(fallback)
        }
      } else if (key === 'modules') {
        config.modules = config.modules.concat(resolveConfig[key])
      } else {
        config[key] = resolveConfig[key]
      }
    })
  }

  return ResolverFactory.createResolver(config);
}

function resolveToFile(resolver, name, parentFile) {
    var projectDir = infer.cx().parent.projectDir;
    var fullParent = path.resolve(projectDir, parentFile);
    try {
      return resolver.resolveSync({}, path.dirname(fullParent), name);
    } catch(e) {
      console.log(e.stack)
      return ''
    }
}

tern.registerPlugin("webpack", function(server, options) {
  var configPath = options.configPath || './webpack.config.js'
  var modules = options.modules || ['node_modules']
  configPath = path.resolve(server.options.projectDir, configPath)
  var resolver = getResolver(modules, configPath)
  server.loadPlugin("commonjs")
  server.loadPlugin("es_modules")
  server.mod.modules.resolvers.push(function (name, parentFile) {
    var resolved = resolveToFile(resolver, name, parentFile)
    return resolved && infer.cx().parent.normalizeFilename(resolved)
  })
})
<|endoftext|>


<commit_before>"use strict";

const {strict: assert} = require("assert");

const {set_global, zrequire} = require("../zjsunit/namespace");
const {make_stub, with_stub} = require("../zjsunit/stub");
const {run_test} = require("../zjsunit/test");
const {make_zjquery} = require("../zjsunit/zjquery");

const noop = function () {};
const return_true = function () {
    return true;
};
set_global("$", make_zjquery());
const _settings_notifications = {
    update_page: () => {},
};
set_global("settings_notifications", _settings_notifications);

set_global("color_data", {});
set_global("current_msg_list", {});
set_global("message_util", {});
set_global("stream_color", {});
set_global("stream_list", {});
set_global("stream_muting", {});
set_global("subs", {});

const peer_data = zrequire("peer_data");
const people = zrequire("people");
zrequire("stream_data");
zrequire("stream_events");
zrequire("Filter", "js/filter");
zrequire("narrow_state");
zrequire("message_view_header");

const george = {
    email: "george@zulip.com",
    full_name: "George",
    user_id: 103,
};
people.add_active_user(george);

const frontend = {
    subscribed: false,
    color: "yellow",
    name: "frontend",
    stream_id: 1,
    is_muted: true,
    invite_only: false,
};

stream_data.add_sub(frontend);

const frontend_filter_terms = [{operator: "stream", operand: "frontend"}];

const frontend_filter = new Filter(frontend_filter_terms);

run_test("update_property", (override) => {
    // Invoke error for non-existent stream/property
    {
        let errors = 0;
        override("blueslip.warn", () => {
            errors += 1;
        });

        stream_events.update_property(99, "color", "blue");
        assert.equal(errors, 1);

        stream_events.update_property(1, "not_real", 42);
        assert.equal(errors, 2);
    }

    // Test update color
    {
        with_stub((stub) => {
            override("stream_color.update_stream_color", stub.f);
            stream_events.update_property(1, "color", "blue");
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.equal(args.val, "blue");
        });
    }

    // Test in home view
    {
        with_stub((stub) => {
            override("stream_muting.update_is_muted", stub.f);
            stream_events.update_property(1, "in_home_view", false);
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.equal(args.val, true);
        });
    }

    // Test desktop notifications
    stream_events.update_property(1, "desktop_notifications", true);
    assert.equal(frontend.desktop_notifications, true);
    let checkbox = $("#desktop_notifications_1");
    assert.equal(checkbox.prop("checked"), true);

    // Tests audible notifications
    stream_events.update_property(1, "audible_notifications", true);
    assert.equal(frontend.audible_notifications, true);
    checkbox = $("#audible_notifications_1");
    assert.equal(checkbox.prop("checked"), true);

    // Tests push notifications
    stream_events.update_property(1, "push_notifications", true);
    assert.equal(frontend.push_notifications, true);
    checkbox = $("#push_notifications_1");
    assert.equal(checkbox.prop("checked"), true);

    // Tests email notifications
    stream_events.update_property(1, "email_notifications", true);
    assert.equal(frontend.email_notifications, true);
    checkbox = $("#email_notifications_1");
    assert.equal(checkbox.prop("checked"), true);

    // Tests wildcard_mentions_notify notifications
    stream_events.update_property(1, "wildcard_mentions_notify", true);
    assert.equal(frontend.wildcard_mentions_notify, true);
    checkbox = $("#wildcard_mentions_notify_1");
    assert.equal(checkbox.prop("checked"), true);

    // Test name change
    {
        with_stub((stub) => {
            override("subs.update_stream_name", stub.f);
            stream_events.update_property(1, "name", "the frontend");
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.equal(args.val, "the frontend");
        });
    }

    // Test description change
    {
        with_stub((stub) => {
            override("subs.update_stream_description", stub.f);
            stream_events.update_property(1, "description", "we write code", {
                rendered_description: "we write code",
            });
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.equal(args.val, "we write code");
        });
    }

    // Test email address change
    stream_events.update_property(1, "email_address", "zooly@zulip.com");
    assert.equal(frontend.email_address, "zooly@zulip.com");

    // Test pin to top
    {
        override("stream_list.refresh_pinned_or_unpinned_stream", noop);
        stream_events.update_property(1, "pin_to_top", true);
        checkbox = $("#pin_to_top_1");
        assert.equal(checkbox.prop("checked"), true);
    }

    // Test stream privacy change event
    {
        with_stub((stub) => {
            override("subs.update_stream_privacy", stub.f);
            stream_events.update_property(1, "invite_only", true, {
                history_public_to_subscribers: true,
            });
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.deepEqual(args.val, {
                invite_only: true,
                history_public_to_subscribers: true,
            });
        });
    }

    // Test stream stream_post_policy change event
    {
        with_stub((stub) => {
            override("subs.update_stream_post_policy", stub.f);
            stream_events.update_property(
                1,
                "stream_post_policy",
                stream_data.stream_post_policy_values.admins.code,
            );
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.equal(args.val, stream_data.stream_post_policy_values.admins.code);
        });
    }

    // Test stream message_retention_days change event
    {
        with_stub((stub) => {
            override("subs.update_message_retention_setting", stub.f);
            stream_events.update_property(1, "message_retention_days", 20);
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, 1);
            assert.equal(args.val, 20);
        });
    }
});

run_test("marked_subscribed", (override) => {
    override("stream_data.subscribe_myself", noop);
    override("stream_data.update_calculated_fields", noop);

    // Test undefined error
    {
        let errors = 0;
        override("blueslip.error", () => {
            errors += 1;
        });
        stream_events.mark_subscribed(undefined, [], "yellow");
        assert.equal(errors, 1);
    }

    // Test early return if subscribed
    {
        let completed = false;
        override("message_util.do_unread_count_updates", () => {
            completed = true; // This gets run if we continue and don't early return
        });
        const subscribed = {subscribed: true};
        stream_events.mark_subscribed(subscribed, [], "yellow");
        assert.equal(completed, false);

        // prevent warning about spurious override
        message_util.do_unread_count_updates();
    }

    set_global("message_list", {
        all: {
            all_messages() {
                return ["msg"];
            },
        },
    });

    set_global("subs", {update_settings_for_subscribed: noop});
    set_global("overlays", {streams_open: return_true});

    // Test basic dispatching and updating stream color
    narrow_state.set_current_filter(frontend_filter);
    {
        let args;
        let list_updated = false;

        const stream_list_stub = make_stub();
        const message_view_header_stub = make_stub();
        const message_util_stub = make_stub();

        override("stream_color.update_stream_color", noop);
        override("stream_list.add_sidebar_row", stream_list_stub.f);
        override("message_util.do_unread_count_updates", message_util_stub.f);
        override("message_view_header.render_title_area", message_view_header_stub.f);
        override("current_msg_list.update_trailing_bookend", () => {
            list_updated = true;
        });

        stream_events.mark_subscribed(frontend, [], "blue");

        args = message_util_stub.get_args("messages");
        assert.deepEqual(args.messages, ["msg"]);

        args = stream_list_stub.get_args("sub");
        assert.equal(args.sub.stream_id, 1);
        assert.equal(message_view_header_stub.num_calls, 1);

        assert.equal(list_updated, true);

        assert.equal(frontend.color, "blue");
    }
    narrow_state.reset_current_filter();

    // Test assigning generated color
    {
        frontend.color = undefined;
        override("color_data.pick_color", () => "green");
        let warnings = 0;
        override("blueslip.warn", () => {
            warnings += 1;
        });

        // narrow state is undefined
        with_stub((stub) => {
            override("message_util.do_unread_count_updates", noop);
            override("stream_list.add_sidebar_row", noop);
            override("subs.set_color", stub.f);
            stream_events.mark_subscribed(frontend, [], undefined);
            const args = stub.get_args("id", "color");
            assert.equal(args.id, 1);
            assert.equal(args.color, "green");
            assert.equal(warnings, 1);
        });
    }

    // Test assigning subscriber emails
    // narrow state is undefined
    {
        override("stream_color.update_stream_color", noop);
        override("message_util.do_unread_count_updates", noop);
        override("stream_list.add_sidebar_row", noop);

        const user_ids = [15, 20, 25];
        stream_events.mark_subscribed(frontend, user_ids, "");
        assert.deepEqual(new Set(peer_data.get_subscribers(frontend.stream_id)), new Set(user_ids));

        // assign self as well
        with_stub((stub) => {
            override("stream_data.subscribe_myself", stub.f);
            stream_events.mark_subscribed(frontend, [], "");
            const args = stub.get_args("sub");
            assert.deepEqual(frontend, args.sub);
        });

        // and finally update subscriber settings
        with_stub((stub) => {
            override("subs.update_settings_for_subscribed", stub.f);
            stream_events.mark_subscribed(frontend, [], "");
            const args = stub.get_args("sub");
            assert.deepEqual(frontend, args.sub);
        });
    }
});

run_test("mark_unsubscribed", (override) => {
    override("stream_data.update_calculated_fields", noop);

    {
        let removed_sub = false;
        override("stream_list.remove_sidebar_row", () => {
            removed_sub = true;
        });

        // take no action if no sub specified
        stream_events.mark_unsubscribed();
        assert.equal(removed_sub, false);

        // take no action if already unsubscribed
        frontend.subscribed = false;
        stream_events.mark_unsubscribed(frontend);
        assert.equal(removed_sub, false);

        // prevent warning about spurious mock
        stream_list.remove_sidebar_row();
    }

    // Test unsubscribe
    frontend.subscribed = true;
    {
        with_stub((stub) => {
            override("stream_data.unsubscribe_myself", stub.f);
            override("subs.update_settings_for_unsubscribed", noop);
            override("stream_list.remove_sidebar_row", noop);
            stream_events.mark_unsubscribed(frontend);
            const args = stub.get_args("sub");
            assert.deepEqual(args.sub, frontend);
        });
    }

    // Test update settings after unsubscribe
    {
        with_stub((stub) => {
            override("subs.update_settings_for_unsubscribed", stub.f);
            override("stream_data.unsubscribe_myself", noop);
            override("stream_list.remove_sidebar_row", noop);
            stream_events.mark_unsubscribed(frontend);
            const args = stub.get_args("sub");
            assert.deepEqual(args.sub, frontend);
        });
    }

    // Test update bookend and remove done event
    narrow_state.set_current_filter(frontend_filter);
    {
        const message_view_header_stub = make_stub();
        override("message_view_header.render_title_area", message_view_header_stub.f);
        override("stream_data.unsubscribe_myself", noop);
        override("subs.update_settings_for_unsubscribed", noop);

        let updated = false;
        override("current_msg_list.update_trailing_bookend", () => {
            updated = true;
        });

        let event_triggered = false;
        override("stream_list.remove_sidebar_row", (stream_id) => {
            assert.equal(stream_id, frontend.stream_id);
            event_triggered = true;
        });

        stream_events.mark_unsubscribed(frontend);

        assert.equal(message_view_header_stub.num_calls, 1);
        assert.equal(updated, true);
        assert.equal(event_triggered, true);
    }
    narrow_state.reset_current_filter();
});

stream_data.clear_subscriptions();
const dev_help = {
    subscribed: true,
    color: "blue",
    name: "dev help",
    stream_id: 2,
    is_muted: true,
    invite_only: false,
};
stream_data.add_sub(dev_help);

run_test("remove_deactivated_user_from_all_streams", () => {
    const subs_stub = make_stub();
    subs.update_subscribers_ui = subs_stub.f;

    dev_help.can_access_subscribers = true;

    // assert starting state
    assert(!stream_data.is_user_subscribed(dev_help.stream_id, george.user_id));

    // verify that deactivating user should unsubscribe user from all streams
    peer_data.add_subscriber(dev_help.stream_id, george.user_id);
    assert(stream_data.is_user_subscribed(dev_help.stream_id, george.user_id));

    stream_events.remove_deactivated_user_from_all_streams(george.user_id);

    // verify that we issue a call to update subscriber count/list UI
    assert.equal(subs_stub.num_calls, 1);
});
<commit_msg>node tests: Clean up hard coded stream_ids.<commit_after>"use strict";

const {strict: assert} = require("assert");

const {set_global, zrequire} = require("../zjsunit/namespace");
const {make_stub, with_stub} = require("../zjsunit/stub");
const {run_test} = require("../zjsunit/test");
const {make_zjquery} = require("../zjsunit/zjquery");

const noop = function () {};
const return_true = function () {
    return true;
};
set_global("$", make_zjquery());
const _settings_notifications = {
    update_page: () => {},
};
set_global("settings_notifications", _settings_notifications);

set_global("color_data", {});
set_global("current_msg_list", {});
set_global("message_util", {});
set_global("stream_color", {});
set_global("stream_list", {});
set_global("stream_muting", {});
set_global("subs", {});

const peer_data = zrequire("peer_data");
const people = zrequire("people");
zrequire("stream_data");
zrequire("stream_events");
zrequire("Filter", "js/filter");
zrequire("narrow_state");
zrequire("message_view_header");

const george = {
    email: "george@zulip.com",
    full_name: "George",
    user_id: 103,
};
people.add_active_user(george);

const frontend = {
    subscribed: false,
    color: "yellow",
    name: "frontend",
    stream_id: 101,
    is_muted: true,
    invite_only: false,
};

stream_data.add_sub(frontend);

const frontend_filter_terms = [{operator: "stream", operand: "frontend"}];

const frontend_filter = new Filter(frontend_filter_terms);

run_test("update_property", (override) => {
    const stream_id = frontend.stream_id;

    // Invoke error for non-existent stream/property
    {
        let errors = 0;
        override("blueslip.warn", () => {
            errors += 1;
        });

        stream_events.update_property(99, "color", "blue");
        assert.equal(errors, 1);

        stream_events.update_property(stream_id, "not_real", 42);
        assert.equal(errors, 2);
    }

    // Test update color
    {
        with_stub((stub) => {
            override("stream_color.update_stream_color", stub.f);
            stream_events.update_property(stream_id, "color", "blue");
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.equal(args.val, "blue");
        });
    }

    // Test in home view
    {
        with_stub((stub) => {
            override("stream_muting.update_is_muted", stub.f);
            stream_events.update_property(stream_id, "in_home_view", false);
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.equal(args.val, true);
        });
    }

    function checkbox_for(property) {
        return $(`#${property}_${stream_id}`);
    }

    // Test desktop notifications
    stream_events.update_property(stream_id, "desktop_notifications", true);
    assert.equal(frontend.desktop_notifications, true);
    let checkbox = checkbox_for("desktop_notifications");
    assert.equal(checkbox.prop("checked"), true);

    // Tests audible notifications
    stream_events.update_property(stream_id, "audible_notifications", true);
    assert.equal(frontend.audible_notifications, true);
    checkbox = checkbox_for("audible_notifications");
    assert.equal(checkbox.prop("checked"), true);

    // Tests push notifications
    stream_events.update_property(stream_id, "push_notifications", true);
    assert.equal(frontend.push_notifications, true);
    checkbox = checkbox_for("push_notifications");
    assert.equal(checkbox.prop("checked"), true);

    // Tests email notifications
    stream_events.update_property(stream_id, "email_notifications", true);
    assert.equal(frontend.email_notifications, true);
    checkbox = checkbox_for("email_notifications");
    assert.equal(checkbox.prop("checked"), true);

    // Tests wildcard_mentions_notify notifications
    stream_events.update_property(stream_id, "wildcard_mentions_notify", true);
    assert.equal(frontend.wildcard_mentions_notify, true);
    checkbox = checkbox_for("wildcard_mentions_notify");
    assert.equal(checkbox.prop("checked"), true);

    // Test name change
    {
        with_stub((stub) => {
            override("subs.update_stream_name", stub.f);
            stream_events.update_property(stream_id, "name", "the frontend");
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.equal(args.val, "the frontend");
        });
    }

    // Test description change
    {
        with_stub((stub) => {
            override("subs.update_stream_description", stub.f);
            stream_events.update_property(stream_id, "description", "we write code", {
                rendered_description: "we write code",
            });
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.equal(args.val, "we write code");
        });
    }

    // Test email address change
    stream_events.update_property(stream_id, "email_address", "zooly@zulip.com");
    assert.equal(frontend.email_address, "zooly@zulip.com");

    // Test pin to top
    {
        override("stream_list.refresh_pinned_or_unpinned_stream", noop);
        stream_events.update_property(stream_id, "pin_to_top", true);
        checkbox = checkbox_for("pin_to_top");
        assert.equal(checkbox.prop("checked"), true);
    }

    // Test stream privacy change event
    {
        with_stub((stub) => {
            override("subs.update_stream_privacy", stub.f);
            stream_events.update_property(stream_id, "invite_only", true, {
                history_public_to_subscribers: true,
            });
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.deepEqual(args.val, {
                invite_only: true,
                history_public_to_subscribers: true,
            });
        });
    }

    // Test stream stream_post_policy change event
    {
        with_stub((stub) => {
            override("subs.update_stream_post_policy", stub.f);
            stream_events.update_property(
                stream_id,
                "stream_post_policy",
                stream_data.stream_post_policy_values.admins.code,
            );
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.equal(args.val, stream_data.stream_post_policy_values.admins.code);
        });
    }

    // Test stream message_retention_days change event
    {
        with_stub((stub) => {
            override("subs.update_message_retention_setting", stub.f);
            stream_events.update_property(stream_id, "message_retention_days", 20);
            const args = stub.get_args("sub", "val");
            assert.equal(args.sub.stream_id, stream_id);
            assert.equal(args.val, 20);
        });
    }
});

run_test("marked_subscribed", (override) => {
    override("stream_data.subscribe_myself", noop);
    override("stream_data.update_calculated_fields", noop);

    // Test undefined error
    {
        let errors = 0;
        override("blueslip.error", () => {
            errors += 1;
        });
        stream_events.mark_subscribed(undefined, [], "yellow");
        assert.equal(errors, 1);
    }

    // Test early return if subscribed
    {
        let completed = false;
        override("message_util.do_unread_count_updates", () => {
            completed = true; // This gets run if we continue and don't early return
        });
        const subscribed = {subscribed: true};
        stream_events.mark_subscribed(subscribed, [], "yellow");
        assert.equal(completed, false);

        // prevent warning about spurious override
        message_util.do_unread_count_updates();
    }

    set_global("message_list", {
        all: {
            all_messages() {
                return ["msg"];
            },
        },
    });

    set_global("subs", {update_settings_for_subscribed: noop});
    set_global("overlays", {streams_open: return_true});

    // Test basic dispatching and updating stream color
    narrow_state.set_current_filter(frontend_filter);
    {
        let args;
        let list_updated = false;

        const stream_list_stub = make_stub();
        const message_view_header_stub = make_stub();
        const message_util_stub = make_stub();

        override("stream_color.update_stream_color", noop);
        override("stream_list.add_sidebar_row", stream_list_stub.f);
        override("message_util.do_unread_count_updates", message_util_stub.f);
        override("message_view_header.render_title_area", message_view_header_stub.f);
        override("current_msg_list.update_trailing_bookend", () => {
            list_updated = true;
        });

        stream_events.mark_subscribed(frontend, [], "blue");

        args = message_util_stub.get_args("messages");
        assert.deepEqual(args.messages, ["msg"]);

        args = stream_list_stub.get_args("sub");
        assert.equal(args.sub.stream_id, frontend.stream_id);
        assert.equal(message_view_header_stub.num_calls, 1);

        assert.equal(list_updated, true);

        assert.equal(frontend.color, "blue");
    }
    narrow_state.reset_current_filter();

    // Test assigning generated color
    {
        frontend.color = undefined;
        override("color_data.pick_color", () => "green");
        let warnings = 0;
        override("blueslip.warn", () => {
            warnings += 1;
        });

        // narrow state is undefined
        with_stub((stub) => {
            override("message_util.do_unread_count_updates", noop);
            override("stream_list.add_sidebar_row", noop);
            override("subs.set_color", stub.f);
            stream_events.mark_subscribed(frontend, [], undefined);
            const args = stub.get_args("id", "color");
            assert.equal(args.id, frontend.stream_id);
            assert.equal(args.color, "green");
            assert.equal(warnings, 1);
        });
    }

    // Test assigning subscriber emails
    // narrow state is undefined
    {
        override("stream_color.update_stream_color", noop);
        override("message_util.do_unread_count_updates", noop);
        override("stream_list.add_sidebar_row", noop);

        const user_ids = [15, 20, 25];
        stream_events.mark_subscribed(frontend, user_ids, "");
        assert.deepEqual(new Set(peer_data.get_subscribers(frontend.stream_id)), new Set(user_ids));

        // assign self as well
        with_stub((stub) => {
            override("stream_data.subscribe_myself", stub.f);
            stream_events.mark_subscribed(frontend, [], "");
            const args = stub.get_args("sub");
            assert.deepEqual(frontend, args.sub);
        });

        // and finally update subscriber settings
        with_stub((stub) => {
            override("subs.update_settings_for_subscribed", stub.f);
            stream_events.mark_subscribed(frontend, [], "");
            const args = stub.get_args("sub");
            assert.deepEqual(frontend, args.sub);
        });
    }
});

run_test("mark_unsubscribed", (override) => {
    override("stream_data.update_calculated_fields", noop);

    {
        let removed_sub = false;
        override("stream_list.remove_sidebar_row", () => {
            removed_sub = true;
        });

        // take no action if no sub specified
        stream_events.mark_unsubscribed();
        assert.equal(removed_sub, false);

        // take no action if already unsubscribed
        frontend.subscribed = false;
        stream_events.mark_unsubscribed(frontend);
        assert.equal(removed_sub, false);

        // prevent warning about spurious mock
        stream_list.remove_sidebar_row();
    }

    // Test unsubscribe
    frontend.subscribed = true;
    {
        with_stub((stub) => {
            override("stream_data.unsubscribe_myself", stub.f);
            override("subs.update_settings_for_unsubscribed", noop);
            override("stream_list.remove_sidebar_row", noop);
            stream_events.mark_unsubscribed(frontend);
            const args = stub.get_args("sub");
            assert.deepEqual(args.sub, frontend);
        });
    }

    // Test update settings after unsubscribe
    {
        with_stub((stub) => {
            override("subs.update_settings_for_unsubscribed", stub.f);
            override("stream_data.unsubscribe_myself", noop);
            override("stream_list.remove_sidebar_row", noop);
            stream_events.mark_unsubscribed(frontend);
            const args = stub.get_args("sub");
            assert.deepEqual(args.sub, frontend);
        });
    }

    // Test update bookend and remove done event
    narrow_state.set_current_filter(frontend_filter);
    {
        const message_view_header_stub = make_stub();
        override("message_view_header.render_title_area", message_view_header_stub.f);
        override("stream_data.unsubscribe_myself", noop);
        override("subs.update_settings_for_unsubscribed", noop);

        let updated = false;
        override("current_msg_list.update_trailing_bookend", () => {
            updated = true;
        });

        let event_triggered = false;
        override("stream_list.remove_sidebar_row", (stream_id) => {
            assert.equal(stream_id, frontend.stream_id);
            event_triggered = true;
        });

        stream_events.mark_unsubscribed(frontend);

        assert.equal(message_view_header_stub.num_calls, 1);
        assert.equal(updated, true);
        assert.equal(event_triggered, true);
    }
    narrow_state.reset_current_filter();
});

stream_data.clear_subscriptions();
const dev_help = {
    subscribed: true,
    color: "blue",
    name: "dev help",
    stream_id: 2,
    is_muted: true,
    invite_only: false,
};
stream_data.add_sub(dev_help);

run_test("remove_deactivated_user_from_all_streams", () => {
    const subs_stub = make_stub();
    subs.update_subscribers_ui = subs_stub.f;

    dev_help.can_access_subscribers = true;

    // assert starting state
    assert(!stream_data.is_user_subscribed(dev_help.stream_id, george.user_id));

    // verify that deactivating user should unsubscribe user from all streams
    peer_data.add_subscriber(dev_help.stream_id, george.user_id);
    assert(stream_data.is_user_subscribed(dev_help.stream_id, george.user_id));

    stream_events.remove_deactivated_user_from_all_streams(george.user_id);

    // verify that we issue a call to update subscriber count/list UI
    assert.equal(subs_stub.num_calls, 1);
});
<|endoftext|>


<commit_before>console.assert(add(0, 0), /* FILL ME IN */);
console.assert(add(0, 1), /* FILL ME IN */);
console.assert(add(0, 2), /* FILL ME IN */);
console.assert(minus(0, 3), /* FILL ME IN */);
console.assert(minus(6, 4), /* FILL ME IN */);
console.assert(minus(7, 5), /* FILL ME IN */);
console.assert(minus(3, 6), /* FILL ME IN */);
console.assert(times(0, 7), /* FILL ME IN */);
console.assert(times(0, 8), /* FILL ME IN */);
console.assert(divide(0, 9), /* FILL ME IN */);
console.assert(divide(1, 1), /* FILL ME IN */);
console.assert(divide(2, 5), /* FILL ME IN */);
/**
 * @param {Number} A to add to B
 * @param {Number} B to add to A
 * @return {Number} sum of A and B
 */
function add(A,B){
  return A + B;
} // END add

function minus(A,B) {
  return A-B;
}

function times (A,B) {
  return A*B;
}

function divide (A,B) {
  return A/B;
}
<commit_msg>Adeed four Math functions wuth assertion messages<commit_after>console.assert(add(0, 0) === 0, "Asserting value of 0");
console.assert(add(0, 1) === 1, "Asserting value of 1");
console.assert(add(0, 2), "Addition");
console.assert(add(0, 3), "Addition");
console.assert(add(0, 4), "Addition");
console.assert(add(0, 5), "Addition");
console.assert(add(0, 6), "Addition");
console.assert(add(0, 7), "Addition");
console.assert(add(0, 8), "Addition");
console.assert(add(0, 9), "Addition");
console.assert(add(1, 0), "Addition");
console.assert(add(1, 1), "Addition");
console.assert(add(1, 2), "Addition");
console.assert(add(1, 3), "Addition");
console.assert(add(1, 4), "Addition");
console.assert(add(1, 5), "Addition");
console.assert(add(1, 6), "Addition");
console.assert(add(1, 7), "Addition");
console.assert(add(1, 8), "Addition");
console.assert(add(1, 9), "Addition");
console.assert(add(2, 0), "Addition");
console.assert(add(2, 1), "Addition");
console.assert(add(2, 2), "Addition");
console.assert(add(2, 3), "Addition");
console.assert(add(2, 4), "Addition");
console.assert(add(2, 5), "Addition");
console.assert(add(2, 6), "Addition");
console.assert(add(2, 7), "Addition");
console.assert(add(2, 8), "Addition");
console.assert(add(2, 9), "Addition");

console.assert(minus(0, 1) === -1, "Asserting value of -1");
console.assert(minus(5, 4) === 1, "Asserting value of 1");
console.assert(minus(8, 5) === 3, "Asserting value of 3");
console.assert(minus(9, 6) === 3, "Asserting value of 3");

console.assert(times(0, 7) === 0, "Asserting value of 0");
console.assert(times(1, 8) === 8, "Asserting value of 8");

console.assert(divide(9, 1) === 9, "Asserting value of 9");
console.assert(divide(8, 2) === 4, "Asserting value of 4");
console.assert(divide(5, 0) === Infinity, "Asserting value of infinity");

/**
 * @param {Number} A to add to B
 * @param {Number} B to add to A
 * @return {Number} sum of A and B
 */
function add(A,B){
  return A + B;
} // END add

function minus(A,B) {
  return A-B;
}

function times (A,B) {
  return A*B;
}

function divide (A,B) {
  return A/B;
}
<|endoftext|>