<commit_before>'use strict';

/**
 * Define the SocketManager which manage connexions between
 * clients and devices
 */
var util = require('util');
var events = require('events');
var socket = require('socket.io');
var DeviceModel = process.requireManage('app/server/models/DeviceModel.js');
var DeviceListener = process.requireManage('app/server/eventListeners/DeviceListener');
var ClientListener = process.requireManage('app/server/eventListeners/ClientListener');
var ScheduleManager = process.requireManage('app/server/services/ScheduleManager.js');

/**
 * Defines a custom error with an error code.
 *
 * @class SocketError
 * @constructor
 * @extends Error
 * @param {String} message The error message
 * @param {String} code The error code
 */
function SocketError(message, code) {
  this.name = 'SocketError';
  this.message = message || '';
  this.code = code;
}

util.inherits(SocketError, Error);

/**
 * Initialize a socket.io connexions between client and devices
 *
 * @class SocketProvider
 * @constructor
 * @param {namespace} namespace The namespace for the socket device
 */
function SocketProvider(namespace) {

  /**
   * Start a socket.io server on port 3002
   *
   * @property io
   * @type Object
   */
  this.io = socket('3002');

  /**
   * The client socket
   *
   * @property ioClient
   * @type Object
   */
  this.ioClient = this.io.of('/client');

  /**
   * The device socket
   *
   * @property ioDevice
   * @type Object
   */
  this.ioDevice = this.io.of(namespace);

  /**
   * Device model
   *
   * @property deviceModel
   * @type DeviceModel
   */
  this.deviceModel = new DeviceModel();

  /**
   * devices connected
   *
   * @property devices
   * @type Array
   */
  this.devices = [];

  /**
   * The device listener
   *
   * @property deviceListener
   * @type DeviceListener
   */
  this.deviceListener = new DeviceListener(this.deviceModel);

  /**
   * The client listener
   *
   * @property clientListener
   * @type ClientListener
   */
  this.clientListener = new ClientListener(this.ioClient);

  /**
   * The schedule manager
   *
   * @property scheduleManager
   * @type ScheduleManager
   */
  this.scheduleManager = new ScheduleManager();
}

util.inherits(SocketProvider, events.EventEmitter);
module.exports = SocketProvider;

/**
 * Retrieve a device index with its socket id
 *
 * @method findDeviceIndexById
 * @private
 * @param {String} deviceId The device id
 * @returns {int | -1}
 */
function findDeviceIndexById(deviceId) {
  return this.devices.findIndex(function(value) {
    return value.device.id == deviceId;
  });
}

/**
 * Retrieve a device with its socket id
 *
 * @method findDevice
 * @private
 * @param {String} socketId The socket id
 * @returns {Object | null}
 */
function findDevice(socketId) {
  var device;
  var data = this.devices.find(function(value) {
    return value.socketId == socketId;
  });

  if (typeof data !== 'undefined') {
    device = data.device;
  }

  return device;
}

/**
 * Retrieve a device with its id
 *
 * @method findDeviceById
 * @private
 * @param {String} deviceId The device id
 * @returns {Object | null}
 */
function findDeviceById(deviceId) {
  var device;
  var data = this.devices.find(function(value) {
    return value.device.id == deviceId;
  });

  if (typeof data !== 'undefined') {
    device = data.device;
  }

  return device;
}

/**
 * Retrieve a socket with a device id
 *
 * @method findSocket
 * @private
 * @param {String} deviceId The device id
 * @returns {Object | null}
 */
function findSocket(deviceId) {
  var data = this.devices.find(function(value) {
    return value.device.id == deviceId;
  });

  if (typeof data !== 'undefined') {
    return this.ioDevice.sockets[data.socketId];
  }

  return null;
}

/**
 * Store a new device in-memory
 *
 * @method addDevice
 * @private
 * @param {String} socketId the socket id
 * @param {Object} device The new device
 * @param {String} ip The address IP of the device
 */
function addDevice(socketId, device, ip) {
  var index = findDeviceIndexById.call(this, device.id);

  device.ip = ip.substring(7);

  if (index === -1) {
    this.devices.push({
      socketId: socketId,
      device: device
    });
  } else {
    this.devices[index] = {
      socketId: socketId,
      device: device
    };
  }
}

/**
 * Set the name of a device
 *
 * @method setDeviceName
 * @param {Object} The device object
 * @param {String} name The new name for the device
 */
function setDeviceName(device, name) {
  device.name = name;
}

/**
 * Set a percent with the storage data
 *
 * @method setDeviceStorage
 * @param {Object} The device object
 * @param {Object} storage The storage data for the device
 */
function setDeviceStorage(device, storage) {
  device.storage = {
    free: parseInt(storage.free) / 1000000000,
    used: parseInt(storage.used) / 1000000000,
    total: (parseInt(storage.free) + parseInt(storage.used)) / 1000000000,
    percent: (parseInt(storage.used) /
    (parseInt(storage.free) + parseInt(storage.used))) * 100
  };
}

/**
 * Set the inputs of a device
 *
 * @method setDeviceInputs
 * @param {Object} The device object
 * @param {Object} inputs The inputs (camera, slides) connected to the device
 */
function setDeviceInputs(device, inputs) {
  var data = {};

  if (inputs.camera.timings) {
    if (!inputs.camera.timings.supported) {
      data.camera = 'ko';
    } else {
      data.camera = 'ok';
    }
  } else {
    data.camera = 'disconnected';
  }
  if (inputs.slides.timings) {
    if (!inputs.slides.timings.supported) {
      data.desktop = 'ko';
    } else {
      data.desktop = 'ok';
    }
  } else {
    data.desktop = 'disconnected';
  }

  device.inputs = data;
}

/**
 * Set the presets of a device
 *
 * @method setDevicesPresets
 * @param {Object} The device object
 * @param {Object} presets The presets of the device
 */
function setDevicePresets(device, presets) {
  var data = presets;

  if (!Array.isArray(presets)) {
    presets = [];
    presets.push(data);
  }

  device.presets = presets;
}

/**
 * Update the status of a device
 *
 * @method updateDeviceState
 * @param {Object} The device object
 * @param {Object} status The status of the device
 */
function updateDeviceState(device, status) {
  device.status = status;
}

/**
 * Update an in-memory stored device when a device is disconnected
 *
 * @method disconnectDevice
 * @private
 * @param {Object} The device object
 */
 function disconnectDevice(device) {
  device.status = 'disconnected';
  delete device.presets;
  delete device.inputs;
  delete device.storage;
}

/**
 * Initialize a socket.io client connexion
 *
 * @method clientConnect
 * @private
 */
function clientConnect() {
  var self = this;

  this.ioClient.on('connection', function(socket) {

    // Listening for device needed updating settings (storage, inputs, presets)
    socket.on('settings', function(data) {
      self.devicesSettings(data);
    });

    // Listening for device update name
    socket.on('update.name', function(data) {
      var socket = findSocket.call(self, data.id);

      // Send the new device name
      if (socket)
        self.deviceListener.updateName(socket, data.name);
    });

    // Listening for start a new recording session
    socket.on('session.start', function(data) {
      var sockets = [];

      data.deviceIds.map(function(id) {
        sockets.push(findSocket.call(self, id));
      });

      self.deviceListener.startRecord(sockets, data, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        }
      });
    });

    // Listening for stop the currently running recording session
    socket.on('session.stop', function(data) {
      var sockets = [];

      data.deviceIds.map(function(id) {
        sockets.push(findSocket.call(self, id));
      });

      self.deviceListener.stopRecord(sockets, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        }
      });
    });

    // Listening for tag the currently running recording session
    socket.on('session.index', function(data) {
      var sockets = [];

      data.deviceIds.map(function(id) {
        sockets.push(findSocket.call(self, id));
      });

      self.deviceListener.tagRecord(sockets, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        }
      });
    });
  });
}

/**
 * Initialize a socket.io device connexion
 *
 * @method deviceConnect
 * @private
 */
function deviceConnect() {
  var self = this;

  this.ioDevice.on('connection', function(socket) {

    // Event on device start
    socket.on('hello', function(data) {
      self.deviceListener.hello(data, socket, function(error, device) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        } else {
          addDevice.call(self, socket.id, device, socket.handshake.address);

          // Update the scheduled jobs
          self.scheduleManager.updateJobs.call(self, device, function(error, schedules) {
            if (error)
              self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
            if (!device.group)
              device.schedules = schedules;
          });
        }
      });
    });

    // Listening for device name
    socket.on('settings.name', function(name) {
      var device = findDevice.call(self, socket.id);

      // Verify if the name is defined
      if (!name) {
        name = 'Device';
      }

      self.deviceListener.setName(name, device.id, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        } else if (device.name.length === 0 && device.state === DeviceModel.STATE_PENDING) {
          setDeviceName.call(self, device, name);
          self.clientListener.hello(device);
        }
      });
    });

    // Listening for device storage
    socket.on('storage', function(data) {
      var device = findDevice.call(self, socket.id);

      setDeviceStorage.call(self, device, data);
      self.clientListener.update('storage', device.storage, device.id);
    });

    // Listening for device inputs
    socket.on('inputs', function(data) {
      var device = findDevice.call(self, socket.id);

      setDeviceInputs.call(self, device, data);
      self.clientListener.update('inputs', device.inputs, device.id);
    });

    // Listening for device equipments
    socket.on('settings.presets', function(data) {
      var device = findDevice.call(self, socket.id);

      setDevicePresets.call(self, device, data);
      self.clientListener.update('presets', device.presets, device.id);
    });

    // Listening for device status change
    socket.on('session.status', function(data) {
      var device = findDevice.call(self, socket.id);

      updateDeviceState.call(self, device, data.status);
      self.clientListener.update('status', data.status, device.id);
    });

    // Disconnect event
    socket.on('disconnect', function() {
      var device = findDevice.call(self, socket.id);

      disconnectDevice.call(self, device);
      self.clientListener.update('status', 'disconnected', device.id);
    });
  });
}

/**
 * Instantiate all socket.io connexions
 *
 * @method connect
 * @async
 * @param {Function} callback Function to call when it's done with :
 *  - **Error** An error if something went wrong, null otherwise
 */
SocketProvider.prototype.connect = function(callback) {
  var self = this;

  deviceConnect.call(this);
  clientConnect.call(this);

  // Prepare cache on server start
  this.deviceModel.get(null, function(error, entities) {
    if (error)
      callback(error);

    entities.map(function(device) {
      device.status = 'disconnected';

      self.devices.push({
        socketId: null,
        device: device
      });
    });
    callback();
  });

};

/**
 * Get all devices
 *
 * @method getDevices
 * @returns {Array} An array of devices
 */
SocketProvider.prototype.getDevices = function() {
  var devices = [];

  this.devices.map(function(data) {
    devices.push(data.device);
  });

  return devices;
};

/**
 * Get a socket with its id
 *
 * @method getSockets
 * @param {String} id The device id
 * @returns {Object} The socket linked to the device id
 */
SocketProvider.prototype.getSocket = function(id) {
  return findSocket.call(this, id);
};

/**
 * Asks for devices storage
 *
 * @method devicesSettings
 * @param {Array} deviceIds An array of device ids
 */
SocketProvider.prototype.devicesSettings = function(deviceIds) {
  var self = this,
    socket;

  deviceIds.map(function(id) {
    socket = findSocket.call(self, id);

    // Asks for device settings
    if (socket)
      self.deviceListener.settings(socket);
  });
};

/**
 * Update a device with the given data
 *
 * @method updateDevice
 * @param {String} deviceId the device id
 * @param {Object} data The new data of the device
 */
SocketProvider.prototype.updateDevice = function(deviceId, data) {
  var device = findDeviceById.call(this, deviceId),
    self = this;

  for (var key in data) {
    device[key] = data[key];
    self.clientListener.update(key, data[key], device.id);
  }
};

/**
 * Remove an in-memory stored device with its id
 *
 * @method removeDeviceById
 * @param {String} deviceId The device id
 */
SocketProvider.prototype.removeDeviceById = function(deviceId) {
  var index = findDeviceIndexById.call(this, deviceId);

  if (index >= 0) {
    this.devices.splice(index, 1);
  }
};
<commit_msg>Add some verifications on device disconnect + update the settings.name device event<commit_after>'use strict';

/**
 * Define the SocketManager which manage connexions between
 * clients and devices
 */
var util = require('util');
var events = require('events');
var socket = require('socket.io');
var DeviceModel = process.requireManage('app/server/models/DeviceModel.js');
var DeviceListener = process.requireManage('app/server/eventListeners/DeviceListener');
var ClientListener = process.requireManage('app/server/eventListeners/ClientListener');
var ScheduleManager = process.requireManage('app/server/services/ScheduleManager.js');

/**
 * Defines a custom error with an error code.
 *
 * @class SocketError
 * @constructor
 * @extends Error
 * @param {String} message The error message
 * @param {String} code The error code
 */
function SocketError(message, code) {
  this.name = 'SocketError';
  this.message = message || '';
  this.code = code;
}

util.inherits(SocketError, Error);

/**
 * Initialize a socket.io connexions between client and devices
 *
 * @class SocketProvider
 * @constructor
 * @param {namespace} namespace The namespace for the socket device
 */
function SocketProvider(namespace) {

  /**
   * Start a socket.io server on port 3002
   *
   * @property io
   * @type Object
   */
  this.io = socket('3002');

  /**
   * The client socket
   *
   * @property ioClient
   * @type Object
   */
  this.ioClient = this.io.of('/client');

  /**
   * The device socket
   *
   * @property ioDevice
   * @type Object
   */
  this.ioDevice = this.io.of(namespace);

  /**
   * Device model
   *
   * @property deviceModel
   * @type DeviceModel
   */
  this.deviceModel = new DeviceModel();

  /**
   * devices connected
   *
   * @property devices
   * @type Array
   */
  this.devices = [];

  /**
   * The device listener
   *
   * @property deviceListener
   * @type DeviceListener
   */
  this.deviceListener = new DeviceListener(this.deviceModel);

  /**
   * The client listener
   *
   * @property clientListener
   * @type ClientListener
   */
  this.clientListener = new ClientListener(this.ioClient);

  /**
   * The schedule manager
   *
   * @property scheduleManager
   * @type ScheduleManager
   */
  this.scheduleManager = new ScheduleManager();
}

util.inherits(SocketProvider, events.EventEmitter);
module.exports = SocketProvider;

/**
 * Retrieve a device index with its socket id
 *
 * @method findDeviceIndexById
 * @private
 * @param {String} deviceId The device id
 * @returns {int | -1}
 */
function findDeviceIndexById(deviceId) {
  return this.devices.findIndex(function(value) {
    return value.device.id == deviceId;
  });
}

/**
 * Retrieve a device with its socket id
 *
 * @method findDevice
 * @private
 * @param {String} socketId The socket id
 * @returns {Object | null}
 */
function findDevice(socketId) {
  var device;
  var data = this.devices.find(function(value) {
    return value.socketId == socketId;
  });

  if (typeof data !== 'undefined') {
    device = data.device;
  }

  return device;
}

/**
 * Retrieve a device with its id
 *
 * @method findDeviceById
 * @private
 * @param {String} deviceId The device id
 * @returns {Object | null}
 */
function findDeviceById(deviceId) {
  var device;
  var data = this.devices.find(function(value) {
    return value.device.id == deviceId;
  });

  if (typeof data !== 'undefined') {
    device = data.device;
  }

  return device;
}

/**
 * Retrieve a socket with a device id
 *
 * @method findSocket
 * @private
 * @param {String} deviceId The device id
 * @returns {Object | null}
 */
function findSocket(deviceId) {
  var data = this.devices.find(function(value) {
    return value.device.id == deviceId;
  });

  if (typeof data !== 'undefined') {
    return this.ioDevice.sockets[data.socketId];
  }

  return null;
}

/**
 * Store a new device in-memory
 *
 * @method addDevice
 * @private
 * @param {String} socketId the socket id
 * @param {Object} device The new device
 * @param {String} ip The address IP of the device
 */
function addDevice(socketId, device, ip) {
  var index = findDeviceIndexById.call(this, device.id);

  device.ip = ip.substring(7);

  if (index === -1) {
    this.devices.push({
      socketId: socketId,
      device: device
    });
  } else {
    this.devices[index] = {
      socketId: socketId,
      device: device
    };
  }
}

/**
 * Set the name of a device
 *
 * @method setDeviceName
 * @param {Object} The device object
 * @param {String} name The new name for the device
 */
function setDeviceName(device, name) {
  device.name = name;
}

/**
 * Set a percent with the storage data
 *
 * @method setDeviceStorage
 * @param {Object} The device object
 * @param {Object} storage The storage data for the device
 */
function setDeviceStorage(device, storage) {
  device.storage = {
    free: parseInt(storage.free) / 1000000000,
    used: parseInt(storage.used) / 1000000000,
    total: (parseInt(storage.free) + parseInt(storage.used)) / 1000000000,
    percent: (parseInt(storage.used) /
    (parseInt(storage.free) + parseInt(storage.used))) * 100
  };
}

/**
 * Set the inputs of a device
 *
 * @method setDeviceInputs
 * @param {Object} The device object
 * @param {Object} inputs The inputs (camera, slides) connected to the device
 */
function setDeviceInputs(device, inputs) {
  var data = {};

  if (inputs.camera.timings) {
    if (!inputs.camera.timings.supported) {
      data.camera = 'ko';
    } else {
      data.camera = 'ok';
    }
  } else {
    data.camera = 'disconnected';
  }
  if (inputs.slides.timings) {
    if (!inputs.slides.timings.supported) {
      data.desktop = 'ko';
    } else {
      data.desktop = 'ok';
    }
  } else {
    data.desktop = 'disconnected';
  }

  device.inputs = data;
}

/**
 * Set the presets of a device
 *
 * @method setDevicesPresets
 * @param {Object} The device object
 * @param {Object} presets The presets of the device
 */
function setDevicePresets(device, presets) {
  var data = presets;

  if (!Array.isArray(presets)) {
    presets = [];
    presets.push(data);
  }

  device.presets = presets;
}

/**
 * Update the status of a device
 *
 * @method updateDeviceState
 * @param {Object} The device object
 * @param {Object} status The status of the device
 */
function updateDeviceState(device, status) {
  device.status = status;
}

/**
 * Update an in-memory stored device when a device is disconnected
 *
 * @method disconnectDevice
 * @private
 * @param {Object} The device object
 */
function disconnectDevice(device) {
  device.status = 'disconnected';
  delete device.presets;
  delete device.inputs;
  delete device.storage;
}

/**
 * Initialize a socket.io client connexion
 *
 * @method clientConnect
 * @private
 */
function clientConnect() {
  var self = this;

  this.ioClient.on('connection', function(socket) {

    // Listening for device needed updating settings (storage, inputs, presets)
    socket.on('settings', function(data) {
      self.devicesSettings(data);
    });

    // Listening for device update name
    socket.on('update.name', function(data) {
      var socket = findSocket.call(self, data.id);

      // Send the new device name
      if (socket)
        self.deviceListener.updateName(socket, data.name);
    });

    // Listening for start a new recording session
    socket.on('session.start', function(data) {
      var sockets = [];

      data.deviceIds.map(function(id) {
        sockets.push(findSocket.call(self, id));
      });

      self.deviceListener.startRecord(sockets, data, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        }
      });
    });

    // Listening for stop the currently running recording session
    socket.on('session.stop', function(data) {
      var sockets = [];

      data.deviceIds.map(function(id) {
        sockets.push(findSocket.call(self, id));
      });

      self.deviceListener.stopRecord(sockets, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        }
      });
    });

    // Listening for tag the currently running recording session
    socket.on('session.index', function(data) {
      var sockets = [];

      data.deviceIds.map(function(id) {
        sockets.push(findSocket.call(self, id));
      });

      self.deviceListener.tagRecord(sockets, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        }
      });
    });
  });
}

/**
 * Initialize a socket.io device connexion
 *
 * @method deviceConnect
 * @private
 */
function deviceConnect() {
  var self = this;

  this.ioDevice.on('connection', function(socket) {

    // Event on device start
    socket.on('hello', function(data) {
      self.deviceListener.hello(data, socket, function(error, device) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        } else {
          addDevice.call(self, socket.id, device, socket.handshake.address);

          // Update the scheduled jobs
          self.scheduleManager.updateJobs.call(self, device, function(error, schedules) {
            if (error)
              self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
            if (!device.group)
              device.schedules = schedules;
          });
        }
      });
    });

    // Listening for device name
    socket.on('settings.name', function(data) {
      var device = findDevice.call(self, socket.id),
        name = data.name;

      // Verify if the name is defined
      if (!name) {
        name = 'Device';
      }

      self.deviceListener.setName(name, device.id, function(error) {
        if (error) {
          self.emit('error', new SocketError(error.message, 'TODO ERROR MESSAGE'));
        } else if (device.name.length === 0 && device.state === DeviceModel.STATE_PENDING) {
          setDeviceName.call(self, device, name);
          self.clientListener.hello(device);
        }
      });
    });

    // Listening for device storage
    socket.on('storage', function(data) {
      var device = findDevice.call(self, socket.id);

      setDeviceStorage.call(self, device, data);
      self.clientListener.update('storage', device.storage, device.id);
    });

    // Listening for device inputs
    socket.on('inputs', function(data) {
      var device = findDevice.call(self, socket.id);

      setDeviceInputs.call(self, device, data);
      self.clientListener.update('inputs', device.inputs, device.id);
    });

    // Listening for device equipments
    socket.on('settings.presets', function(data) {
      var device = findDevice.call(self, socket.id);

      setDevicePresets.call(self, device, data);
      self.clientListener.update('presets', device.presets, device.id);
    });

    // Listening for device status change
    socket.on('session.status', function(data) {
      var device = findDevice.call(self, socket.id);

      updateDeviceState.call(self, device, data.status);
      self.clientListener.update('status', data.status, device.id);
    });

    // Disconnect event
    socket.on('disconnect', function() {
      var device = findDevice.call(self, socket.id);

      if (device) {
        disconnectDevice.call(self, device);
        self.clientListener.update('status', 'disconnected', device.id);
      }
    });
  });
}

/**
 * Instantiate all socket.io connexions
 *
 * @method connect
 * @async
 * @param {Function} callback Function to call when it's done with :
 *  - **Error** An error if something went wrong, null otherwise
 */
SocketProvider.prototype.connect = function(callback) {
  var self = this;

  deviceConnect.call(this);
  clientConnect.call(this);

  // Prepare cache on server start
  this.deviceModel.get(null, function(error, entities) {
    if (error)
      callback(error);

    entities.map(function(device) {
      device.status = 'disconnected';

      self.devices.push({
        socketId: null,
        device: device
      });
    });
    callback();
  });

};

/**
 * Get all devices
 *
 * @method getDevices
 * @returns {Array} An array of devices
 */
SocketProvider.prototype.getDevices = function() {
  var devices = [];

  this.devices.map(function(data) {
    devices.push(data.device);
  });

  return devices;
};

/**
 * Get a socket with its id
 *
 * @method getSockets
 * @param {String} id The device id
 * @returns {Object} The socket linked to the device id
 */
SocketProvider.prototype.getSocket = function(id) {
  return findSocket.call(this, id);
};

/**
 * Asks for devices storage
 *
 * @method devicesSettings
 * @param {Array} deviceIds An array of device ids
 */
SocketProvider.prototype.devicesSettings = function(deviceIds) {
  var self = this,
    socket;

  deviceIds.map(function(id) {
    socket = findSocket.call(self, id);

    // Asks for device settings
    if (socket)
      self.deviceListener.settings(socket);
  });
};

/**
 * Update a device with the given data
 *
 * @method updateDevice
 * @param {String} deviceId the device id
 * @param {Object} data The new data of the device
 */
SocketProvider.prototype.updateDevice = function(deviceId, data) {
  var device = findDeviceById.call(this, deviceId),
    self = this;

  for (var key in data) {
    device[key] = data[key];
    self.clientListener.update(key, data[key], device.id);
  }
};

/**
 * Remove an in-memory stored device with its id
 *
 * @method removeDeviceById
 * @param {String} deviceId The device id
 */
SocketProvider.prototype.removeDeviceById = function(deviceId) {
  var index = findDeviceIndexById.call(this, deviceId);

  if (index >= 0) {
    this.devices.splice(index, 1);
  }
};
<|endoftext|>


<commit_before>/* jshint -W030 */
"use strict";
var common = require('./common');
var sinon = common.sinon;
var nock = common.nock;
var u = common.u;

var Account = require('../lib/resource/Account');
var Group = require('../lib/resource/Group');
var Organization = require('../lib/resource/Organization');
var OrganizationAccountStoreMapping = require('../lib/resource/OrganizationAccountStoreMapping');
var Tenant = require('../lib/resource/Tenant');
var Provider = require('../lib/resource/Provider');
var Directory = require('../lib/resource/Directory');
var DataStore = require('../lib/ds/DataStore');

describe('Resources: ', function () {
  describe('Directory resource', function () {
    var dataStore;

    before(function () {
      dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}});
    });

    describe('get accounts', function () {
      describe('if accounts not set', function () {
        //var directory = new Directory();

        //function getAccountsWithoutHref() {
        //  directory.getAccounts();
        //}

        //it('should throw unhandled exception', function () {
        //  getAccountsWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if accounts are set', function () {
        var sandbox, directory, getResourceStub, cbSpy, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          app = {accounts: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.getAccounts(cbSpy);
          // call with optional param
          directory.getAccounts(opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should get accounts', function () {
          /* jshint -W030 */
          getResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          getResourceStub.should.have.been
            .calledWith(app.accounts.href, null, Account, cbSpy);
          // call with optional param
          getResourceStub.should.have.been
            .calledWith(app.accounts.href, opt, Account, cbSpy);
        });
      });
    });

    describe('create account', function () {
      describe('if accounts not set', function () {
        //var directory = new Directory();

        //function createAccountWithoutHref() {
        //  directory.createAccount();
        //}

        //it('should throw unhandled exception', function () {
        //  createAccountWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if accounts are set', function () {
        var sandbox, directory, createResourceStub, cbSpy, acc, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          acc = {};
          app = {accounts: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          createResourceStub = sandbox.stub(dataStore, 'createResource',
            function (href, options, account, ctor, cb) {
              cb();
            });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.createAccount(acc, cbSpy);
          // call with optional param
          directory.createAccount(acc, opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should create account', function () {
          /* jshint -W030 */
          createResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          createResourceStub.should.have.been
            .calledWith(app.accounts.href, null, acc, Account, cbSpy);
          // call with optional param
          createResourceStub.should.have.been
            .calledWith(app.accounts.href, opt, acc, Account, cbSpy);
        });
      });
    });

    describe('get groups', function () {
      describe('if groups href not set', function () {
        //var directory = new Directory();

        //function getAccountsWithoutHref() {
        //  directory.getGroups();
        //}

        //it('should throw unhandled exception', function () {
        //  getAccountsWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if groups href are set', function () {
        var sandbox, directory, getResourceStub, cbSpy, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          app = {groups: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.getGroups(cbSpy);
          // call with optional param
          directory.getGroups(opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should get groups', function () {
          /* jshint -W030 */
          getResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          getResourceStub.should.have.been
            .calledWith(app.groups.href, null, Group, cbSpy);
          // call with optional param
          getResourceStub.should.have.been
            .calledWith(app.groups.href, opt, Group, cbSpy);
        });
      });
    });

    describe('create group', function () {
      describe('if groups href not set', function () {
        //var directory = new Directory();

        //function createGroupWithoutHref() {
        //  directory.createGroup();
        //}

        //it('should throw unhandled exception', function () {
        //  createGroupWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if groups href are set', function () {
        var sandbox, directory, createResourceStub, cbSpy, group, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          group = {};
          app = {groups: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          createResourceStub = sandbox.stub(dataStore, 'createResource',
            function (href, options, groups, ctor, cb) {
              cb();
            });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.createGroup(group, cbSpy);
          // call with optional param
          directory.createGroup(group, opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should create account', function () {
          /* jshint -W030 */
          createResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          createResourceStub.should.have.been
            .calledWith(app.groups.href, null, group, Group, cbSpy);
          // call with optional param
          createResourceStub.should.have.been
            .calledWith(app.groups.href, opt, group, Group, cbSpy);
        });
      });
    });

    describe('get tenant', function () {
      describe('if tenants href not set', function () {
        //var directory = new Directory();

        //function getAccountsWithoutHref() {
        //  directory.getTenant();
        //}

        //it('should throw unhandled exception', function () {
        //  getAccountsWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if tenants href are set', function () {
        var sandbox, directory, getResourceStub, cbSpy, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          app = {tenant: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.getTenant(cbSpy);
          // call with optional param
          directory.getTenant(opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should get tenants', function () {
          /* jshint -W030 */
          getResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          getResourceStub.should.have.been
            .calledWith(app.tenant.href, null, Tenant, cbSpy);
          // call with optional param
          getResourceStub.should.have.been
            .calledWith(app.tenant.href, opt, Tenant, cbSpy);
        });
      });
    });

    describe('get provider', function(){
      function getProvider(data) {
        return function () {
          var dirObj, providerObj, app, provider;
          before(function (done) {
            // assert
            providerObj = {href: '/provider/href', name: 'provider name'};
            dirObj = {provider: {href: providerObj.href}};
            app = new Directory(dirObj, dataStore);

            nock(u.BASE_URL).get(providerObj.href).reply(200, providerObj);

            var args = [];
            if (data) {
              args.push(data);
            }
            args.push(function cb(err, prov) {
              provider = prov;
              done();
            });

            // act
            app.getProvider.apply(app, args);
          });
          it('should get provider', function () {
            provider.href.should.be.equal(provider.href);
            provider.name.should.be.equal(provider.name);
          });

          it('should be an instance of Provider', function () {
            provider.should.be.an.instanceOf(Provider);
          });
        };
      }

      describe('without options', getProvider());
      describe('with options', getProvider({}));
      describe('if provider not set', function () {
        var accObj, app, cbSpy;
        before(function () {
          // assert
          accObj = {providerData: null};
          app = new Account(accObj, dataStore);
          cbSpy = sinon.spy();

          // act
          app.getProviderData(cbSpy);
        });

        it('should call cb without arguments', function () {
          cbSpy.should.have.been.calledOnce;
          cbSpy.should.have.been.calledWithExactly();
        });
      });
    });

    describe('get organizations', function () {
      describe('if organizations href are set', function () {
        var opt;
        var getResourceStub;
        var sandbox;
        var app;
        var directory;
        var cbSpy;

        before(function () {
          opt = {};
          sandbox = sinon.sandbox.create();
          app = { organizationMappings: { href: 'boom!' } };
          directory = new Directory(app, dataStore);
          cbSpy = sandbox.spy();

          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });

          directory.getOrganizations(cbSpy);
          directory.getOrganizations(opt, cbSpy);
        });

        after(function () {
          sandbox.restore();
        });

        it('should get organizations', function () {
          cbSpy.should.have.been.calledTwice;
          getResourceStub.should.have.been.calledTwice;

          getResourceStub.should.have.been.calledWith(
            app.organizations.href, null, Organization, cbSpy
          );

          getResourceStub.should.have.been.calledWith(
            app.organizations.href, opt, Organization, cbSpy
          );
        });
      });
    });

    describe('get organization mappings', function () {
      describe('if organizationMappings href are set', function () {
        var opt;
        var getResourceStub;
        var sandbox;
        var app;
        var directory;
        var cbSpy;

        before(function () {
          opt = {};
          sandbox = sinon.sandbox.create();
          app = { organizationMappings: { href: 'boom!' } };
          directory = new Directory(app, dataStore);
          cbSpy = sandbox.spy();

          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });

          directory.getOrganizationMappings(cbSpy);
          directory.getOrganizationMappings(opt, cbSpy);
        });

        after(function () {
          sandbox.restore();
        });

        it('should get organization mappings', function () {
          cbSpy.should.have.been.calledTwice;
          getResourceStub.should.have.been.calledTwice;

          getResourceStub.should.have.been.calledWith(
            app.organizationMappings.href, null, OrganizationAccountStoreMapping, cbSpy
          );

          getResourceStub.should.have.been.calledWith(
            app.organizationMappings.href, opt, OrganizationAccountStoreMapping, cbSpy
          );
        });
      });
    });
  });
});
<commit_msg>use `u.v1()` to prepend the version to the url. also make sure to pass the error to `done()` so we know when an error occurs.<commit_after>/* jshint -W030 */
"use strict";
var common = require('./common');
var sinon = common.sinon;
var nock = common.nock;
var u = common.u;

var Account = require('../lib/resource/Account');
var Group = require('../lib/resource/Group');
var Organization = require('../lib/resource/Organization');
var OrganizationAccountStoreMapping = require('../lib/resource/OrganizationAccountStoreMapping');
var Tenant = require('../lib/resource/Tenant');
var Provider = require('../lib/resource/Provider');
var Directory = require('../lib/resource/Directory');
var DataStore = require('../lib/ds/DataStore');

describe('Resources: ', function () {
  describe('Directory resource', function () {
    var dataStore;

    before(function () {
      dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}});
    });

    describe('get accounts', function () {
      describe('if accounts not set', function () {
        //var directory = new Directory();

        //function getAccountsWithoutHref() {
        //  directory.getAccounts();
        //}

        //it('should throw unhandled exception', function () {
        //  getAccountsWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if accounts are set', function () {
        var sandbox, directory, getResourceStub, cbSpy, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          app = {accounts: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.getAccounts(cbSpy);
          // call with optional param
          directory.getAccounts(opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should get accounts', function () {
          /* jshint -W030 */
          getResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          getResourceStub.should.have.been
            .calledWith(app.accounts.href, null, Account, cbSpy);
          // call with optional param
          getResourceStub.should.have.been
            .calledWith(app.accounts.href, opt, Account, cbSpy);
        });
      });
    });

    describe('create account', function () {
      describe('if accounts not set', function () {
        //var directory = new Directory();

        //function createAccountWithoutHref() {
        //  directory.createAccount();
        //}

        //it('should throw unhandled exception', function () {
        //  createAccountWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if accounts are set', function () {
        var sandbox, directory, createResourceStub, cbSpy, acc, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          acc = {};
          app = {accounts: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          createResourceStub = sandbox.stub(dataStore, 'createResource',
            function (href, options, account, ctor, cb) {
              cb();
            });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.createAccount(acc, cbSpy);
          // call with optional param
          directory.createAccount(acc, opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should create account', function () {
          /* jshint -W030 */
          createResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          createResourceStub.should.have.been
            .calledWith(app.accounts.href, null, acc, Account, cbSpy);
          // call with optional param
          createResourceStub.should.have.been
            .calledWith(app.accounts.href, opt, acc, Account, cbSpy);
        });
      });
    });

    describe('get groups', function () {
      describe('if groups href not set', function () {
        //var directory = new Directory();

        //function getAccountsWithoutHref() {
        //  directory.getGroups();
        //}

        //it('should throw unhandled exception', function () {
        //  getAccountsWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if groups href are set', function () {
        var sandbox, directory, getResourceStub, cbSpy, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          app = {groups: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.getGroups(cbSpy);
          // call with optional param
          directory.getGroups(opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should get groups', function () {
          /* jshint -W030 */
          getResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          getResourceStub.should.have.been
            .calledWith(app.groups.href, null, Group, cbSpy);
          // call with optional param
          getResourceStub.should.have.been
            .calledWith(app.groups.href, opt, Group, cbSpy);
        });
      });
    });

    describe('create group', function () {
      describe('if groups href not set', function () {
        //var directory = new Directory();

        //function createGroupWithoutHref() {
        //  directory.createGroup();
        //}

        //it('should throw unhandled exception', function () {
        //  createGroupWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if groups href are set', function () {
        var sandbox, directory, createResourceStub, cbSpy, group, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          group = {};
          app = {groups: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          createResourceStub = sandbox.stub(dataStore, 'createResource',
            function (href, options, groups, ctor, cb) {
              cb();
            });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.createGroup(group, cbSpy);
          // call with optional param
          directory.createGroup(group, opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should create account', function () {
          /* jshint -W030 */
          createResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          createResourceStub.should.have.been
            .calledWith(app.groups.href, null, group, Group, cbSpy);
          // call with optional param
          createResourceStub.should.have.been
            .calledWith(app.groups.href, opt, group, Group, cbSpy);
        });
      });
    });

    describe('get tenant', function () {
      describe('if tenants href not set', function () {
        //var directory = new Directory();

        //function getAccountsWithoutHref() {
        //  directory.getTenant();
        //}

        //it('should throw unhandled exception', function () {
        //  getAccountsWithoutHref.should
        //    .throw(/cannot read property 'href' of undefined/i);
        //});
      });

      describe('if tenants href are set', function () {
        var sandbox, directory, getResourceStub, cbSpy, app, opt;
        before(function () {
          sandbox = sinon.sandbox.create();
          app = {tenant: {href: 'boom!'}};
          opt = {};
          directory = new Directory(app, dataStore);
          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });
          cbSpy = sandbox.spy();

          // call without optional param
          directory.getTenant(cbSpy);
          // call with optional param
          directory.getTenant(opt, cbSpy);
        });
        after(function () {
          sandbox.restore();
        });

        it('should get tenants', function () {
          /* jshint -W030 */
          getResourceStub.should.have.been.calledTwice;
          cbSpy.should.have.been.calledTwice;
          /* jshint +W030 */

          // call without optional param
          getResourceStub.should.have.been
            .calledWith(app.tenant.href, null, Tenant, cbSpy);
          // call with optional param
          getResourceStub.should.have.been
            .calledWith(app.tenant.href, opt, Tenant, cbSpy);
        });
      });
    });

    describe('get provider', function(){
      function getProvider(data) {
        return function () {
          var dirObj, providerObj, app, provider;
          before(function (done) {
            // assert
            providerObj = {href: '/provider/href', name: 'provider name'};
            dirObj = {provider: {href: providerObj.href}};
            app = new Directory(dirObj, dataStore);

            nock(u.BASE_URL).get(u.v1(providerObj.href)).reply(200, providerObj);

            var args = [];
            if (data) {
              args.push(data);
            }
            args.push(function cb(err, prov) {
              provider = prov;
              done(err);
            });

            // act
            app.getProvider.apply(app, args);
          });
          it('should get provider', function () {
            provider.href.should.be.equal(provider.href);
            provider.name.should.be.equal(provider.name);
          });

          it('should be an instance of Provider', function () {
            provider.should.be.an.instanceOf(Provider);
          });
        };
      }

      describe('without options', getProvider());
      describe('with options', getProvider({}));
      describe('if provider not set', function () {
        var accObj, app, cbSpy;
        before(function () {
          // assert
          accObj = {providerData: null};
          app = new Account(accObj, dataStore);
          cbSpy = sinon.spy();

          // act
          app.getProviderData(cbSpy);
        });

        it('should call cb without arguments', function () {
          cbSpy.should.have.been.calledOnce;
          cbSpy.should.have.been.calledWithExactly();
        });
      });
    });

    describe('get organizations', function () {
      describe('if organizations href are set', function () {
        var opt;
        var getResourceStub;
        var sandbox;
        var app;
        var directory;
        var cbSpy;

        before(function () {
          opt = {};
          sandbox = sinon.sandbox.create();
          app = { organizationMappings: { href: 'boom!' } };
          directory = new Directory(app, dataStore);
          cbSpy = sandbox.spy();

          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });

          directory.getOrganizations(cbSpy);
          directory.getOrganizations(opt, cbSpy);
        });

        after(function () {
          sandbox.restore();
        });

        it('should get organizations', function () {
          cbSpy.should.have.been.calledTwice;
          getResourceStub.should.have.been.calledTwice;

          getResourceStub.should.have.been.calledWith(
            app.organizations.href, null, Organization, cbSpy
          );

          getResourceStub.should.have.been.calledWith(
            app.organizations.href, opt, Organization, cbSpy
          );
        });
      });
    });

    describe('get organization mappings', function () {
      describe('if organizationMappings href are set', function () {
        var opt;
        var getResourceStub;
        var sandbox;
        var app;
        var directory;
        var cbSpy;

        before(function () {
          opt = {};
          sandbox = sinon.sandbox.create();
          app = { organizationMappings: { href: 'boom!' } };
          directory = new Directory(app, dataStore);
          cbSpy = sandbox.spy();

          getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) {
            cb();
          });

          directory.getOrganizationMappings(cbSpy);
          directory.getOrganizationMappings(opt, cbSpy);
        });

        after(function () {
          sandbox.restore();
        });

        it('should get organization mappings', function () {
          cbSpy.should.have.been.calledTwice;
          getResourceStub.should.have.been.calledTwice;

          getResourceStub.should.have.been.calledWith(
            app.organizationMappings.href, null, OrganizationAccountStoreMapping, cbSpy
          );

          getResourceStub.should.have.been.calledWith(
            app.organizationMappings.href, opt, OrganizationAccountStoreMapping, cbSpy
          );
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>import React from "react";
import {Form, FormGroup, Label, Input, Button} from 'reactstrap';

const Catform = props => {
return(

        <Form onSubmit={props.handleSubmit}>
            {/* GENDER */}
            <FormGroup>
                What gender do you prefer for your cat?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catsex' className="form-control" >
                <option></option>
                <option value='male'>Male</option>
                <option value='female'>Female</option>
                <option value='any'>Any</option>
                </Input>
            </FormGroup>
            {/* AGE */}
            <FormGroup>
                What age cat would you like?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catage' className="form-control" >
                <option></option>
                <option value='baby'>Baby</option>
                <option value='young'>Young</option>
                <option value='adult'>Adult</option>
                <option value='senior'>Senior</option>
                </Input>
            </FormGroup>
            {/* SIZE */}
            <FormGroup>
                What size dog do you prefer?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catsize' className="form-control" >
                <option></option>
                <option value='small'>Small</option>
                <option value='medium'>Medium</option>
                <option value='large'>Large</option>
                </Input>
            </FormGroup>
            {/* FRIENDLINESS */}
            <FormGroup>
                Does this cat need to be friendly with other pets?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catfriendly' className="form-control" >
                <option></option>
                <option value='yes'>Yes</option>
                <option value='no'>No</option>
                </Input>
            </FormGroup>
            {/* PLAYFUL */}
            <FormGroup>
                Would you like a playful cat?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catplay' className="form-control" >
                <option></option>
                <option value='yes'>Yes</option>
                <option value='no'>No</option>
                </Input>
            </FormGroup>
            {/* ZIP */}
            <FormGroup>
                Zip:
                <Input type="text" name="zip" value={props.value}
                onChange={props.handleOptionChange} />
            </FormGroup>
        <Button onSubmit={() => props.onSubmit} type='submit'>Submit </Button>
        </Form>

    );
}

export default Catform;<commit_msg>updated cat form<commit_after>import React from "react";
import {Form, FormGroup, Label, Input, Button} from 'reactstrap';

const Catform = props => {
return(

        <Form onSubmit={props.handleSubmit}>
            {/* GENDER */}
            <FormGroup>
                What gender do you prefer for your cat?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catsex' className="form-control" >
                <option></option>
                <option value='male'>Male</option>
                <option value='female'>Female</option>
                <option value='any'>Any</option>
                </Input>
            </FormGroup>
            {/* AGE */}
            <FormGroup>
                What age cat would you like?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catage' className="form-control" >
                <option></option>
                <option value='baby'>Baby</option>
                <option value='young'>Young</option>
                <option value='adult'>Adult</option>
                <option value='senior'>Senior</option>
                </Input>
            </FormGroup>
            {/* SIZE */}
            <FormGroup>
                What size cat do you prefer?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catsize' className="form-control" >
                <option></option>
                <option value='small'>Small</option>
                <option value='medium'>Medium</option>
                <option value='large'>Large</option>
                </Input>
            </FormGroup>
            {/* FRIENDLINESS */}
            <FormGroup>
                Does this cat need to be friendly with other pets?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catfriendly' className="form-control" >
                <option></option>
                <option value='yes'>Yes</option>
                <option value='no'>No</option>
                </Input>
            </FormGroup>
            {/* PLAYFUL */}
            <FormGroup>
                Would you like a playful cat?
                <Input type='select' value={props.value} onChange={props.handleOptionChange} name='catplay' className="form-control" >
                <option></option>
                <option value='yes'>Yes</option>
                <option value='no'>No</option>
                </Input>
            </FormGroup>
            {/* ZIP */}
            <FormGroup>
                Zip:
                <Input type="text" name="zip" value={props.value}
                onChange={props.handleOptionChange} />
            </FormGroup>
        <Button onSubmit={() => props.onSubmit} type='submit'>Submit </Button>
        </Form>

    );
}

export default Catform;<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var DebugLogtron = require('debug-logtron');
var fs = require('fs');
var path = require('path');

var TChannelAsThrift = require('tchannel/as/thrift');
var HyperbahnClient = require('tchannel/hyperbahn/index.js');

var source = fs.readFileSync(path.join(__dirname, '../../hyperbahn.thrift'), 'utf8');
var thrift = new TChannelAsThrift({source: source});

module.exports = runTests;

if (require.main === module) {
    runTests(require('../lib/test-cluster.js'));
}

function covertHost(host) {
    var res = '';
    res += ((host.ip.ipv4 & 0xff000000) >> 24) + '.';
    res += ((host.ip.ipv4 & 0xff0000) >> 16) + '.';
    res += ((host.ip.ipv4 & 0xff00) >> 8) + '.';
    res += host.ip.ipv4 & 0xff;
    return res + ':' + host.port;
}

function runTests(HyperbahnCluster) {
    HyperbahnCluster.test('get no host', {
        size: 15
    }, function t(cluster, assert) {
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        thrift.send(request,
            'Hyperbahn::discover',
            null,
            {
                query: {
                    serviceName: 'matt'
                }
            },
            onResponse
        );
        function onResponse(err, res) {
            if (err) {
                assert.end(err);
            }
            assert.ok(res, 'should be a result');
            assert.ok(!res.ok, 'result should be not ok');
            assert.equals(res.body.message, 'no peer available for matt', 'error message as expected');
            assert.end();
        }
    });

    HyperbahnCluster.test('get host port as expected', {
        size: 5
    }, function t(cluster, assert) {
        var bob = cluster.remotes.bob;
        var steve = cluster.remotes.steve;
        var steveSub = steve.channel.subChannels.hyperbahn;
        var request = steveSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });

        var client = new HyperbahnClient({
            serviceName: 'hello-bob',
            callerName: 'hello-bob-test',
            hostPortList: cluster.hostPortList,
            tchannel: bob.channel,
            logger: DebugLogtron('hyperbahnClient')
        });

        client.once('advertised', onResponse);
        client.advertise();

        function onResponse() {
            thrift.send(request,
                'Hyperbahn::discover',
                null,
                {
                    query: {
                        serviceName: 'hello-bob'
                    }
                },
                check
            );
        }

        function check(err, res) {
            if (err) {
                assert.end(err);
            }
            assert.ok(res, 'should be a result');
            assert.ok(res.ok, 'result should be ok');
            assert.equals(covertHost(res.body.peers[0]), bob.channel.hostPort,
                'should get the expected hostPort');
            client.destroy();
            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: empty serviceName', {
        size: 5
    }, function t(cluster, assert) {
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        thrift.send(request,
            'Hyperbahn::discover',
            null,
            {
                query: {
                    serviceName: ''
                }
            },
            onResponse
        );
        function onResponse(err, res) {
            if (err) {
                assert.end(err);
            }
            assert.ok(!res.ok, 'should be not ok');
            assert.equals(res.body.message, 'invalid service name: ', 'error message as expected');
            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: an empty body', {
        size: 5
    }, function t(cluster, assert) {
        cluster.logger.whitelist('warn', 'Got unexpected invalid thrift for arg3');
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        var badSource = fs.readFileSync(path.join(__dirname, 'bad-hyperbahn-empty-req-body.thrift'), 'utf8');
        var badThrift = new TChannelAsThrift({source: badSource});
        badThrift.send(request,
            'Hyperbahn::discover',
            null,
            {},
            onResponse
        );
        function onResponse(err, res) {
            assert.ok(err, 'should be error');
            assert.equals(err.type, 'tchannel.bad-request', 'error should be bad request');
            assert.ok(err.message.indexOf(
                'tchannel-thrift-handler.parse-error.body-failed: Could not parse body (arg3) argument.\n' +
                'Expected Thrift encoded arg3 for endpoint Hyperbahn::discover.') === 0,
                'error message should be a parsing failure');
            assert.ok(err.message.indexOf(
                'Parsing error was: missing required field "query" with id 1 on discover_args') !== -1,
                'error message should be a parsing failure');

            var items = cluster.logger.items();
            console.log('items', items);
            assert.ok(items.length > 0 && items[0].msg === 'Got unexpected invalid thrift for arg3',
                'Do not miss the error log');

            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: a body with a query without the serviceName field', {
        size: 5
    }, function t(cluster, assert) {
        cluster.logger.whitelist('warn', 'Got unexpected invalid thrift for arg3');
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        var badSource = fs.readFileSync(path.join(__dirname, 'bad-hyperbahn-empty-req-body.thrift'), 'utf8');
        var badThrift = new TChannelAsThrift({source: badSource});
        badThrift.send(request,
            'Hyperbahn::discover',
            null,
            {query: {}},
            onResponse
        );
        function onResponse(err, res) {
            assert.ok(err, 'should be error');
            assert.equals(err.type, 'tchannel.bad-request', 'error should be bad request');
            assert.ok(err.message.indexOf(
                'tchannel-thrift-handler.parse-error.body-failed: Could not parse body (arg3) argument.\n' +
                'Expected Thrift encoded arg3 for endpoint Hyperbahn::discover.') === 0,
                'error message should be a parsing failure');
            assert.ok(err.message.indexOf(
                'Parsing error was: missing required field "query" with id 1 on discover_args') !== -1,
                'error message should be a parsing failure');

            var items = cluster.logger.items();
            assert.ok(items.length > 0 && items[0].msg === 'Got unexpected invalid thrift for arg3',
                'Do not miss the error log');

            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: empty serviceName with no exception defined', {
        size: 5
    }, function t(cluster, assert) {
        cluster.logger.whitelist('warn', 'Got unexpected invalid thrift for arg3');
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        var badSource = fs.readFileSync(path.join(__dirname, 'bad-hyperbahn-no-exception.thrift'), 'utf8');
        var badThrift = new TChannelAsThrift({source: badSource});
        badThrift.send(request,
            'Hyperbahn::discover',
            null,
            {
                query: {
                    serviceName: ''
                }
            },
            onResponse
        );
        function onResponse(err, res) {
            assert.ok(err, 'should be error');
            assert.equals(err.type, 'tchannel-thrift-handler.parse-error.body-failed',
                'error should be parser error');
            assert.ok(err.message.indexOf(
                'Could not parse body (arg3) argument.\n' +
                'Expected Thrift encoded arg3 for endpoint Hyperbahn::discover.') === 0,
                'error message should be a parsing failure');

            var items = cluster.logger.items();
            assert.ok(items.length > 0 && items[0].msg === 'Got unexpected invalid thrift for arg3',
                'Do not miss the error log');

            assert.end();
        }
    });
}
<commit_msg>fix lint<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var DebugLogtron = require('debug-logtron');
var fs = require('fs');
var path = require('path');

var TChannelAsThrift = require('tchannel/as/thrift');
var HyperbahnClient = require('tchannel/hyperbahn/index.js');

var source = fs.readFileSync(path.join(__dirname, '../../hyperbahn.thrift'), 'utf8');
var thrift = new TChannelAsThrift({source: source});

module.exports = runTests;

if (require.main === module) {
    runTests(require('../lib/test-cluster.js'));
}

function covertHost(host) {
    var res = '';
    res += ((host.ip.ipv4 & 0xff000000) >> 24) + '.';
    res += ((host.ip.ipv4 & 0xff0000) >> 16) + '.';
    res += ((host.ip.ipv4 & 0xff00) >> 8) + '.';
    res += host.ip.ipv4 & 0xff;
    return res + ':' + host.port;
}

function runTests(HyperbahnCluster) {
    HyperbahnCluster.test('get no host', {
        size: 15
    }, function t(cluster, assert) {
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        thrift.send(request,
            'Hyperbahn::discover',
            null,
            {
                query: {
                    serviceName: 'matt'
                }
            },
            onResponse
        );
        function onResponse(err, res) {
            if (err) {
                assert.end(err);
            }
            assert.ok(res, 'should be a result');
            assert.ok(!res.ok, 'result should be not ok');
            assert.equals(res.body.message, 'no peer available for matt', 'error message as expected');
            assert.end();
        }
    });

    HyperbahnCluster.test('get host port as expected', {
        size: 5
    }, function t(cluster, assert) {
        var bob = cluster.remotes.bob;
        var steve = cluster.remotes.steve;
        var steveSub = steve.channel.subChannels.hyperbahn;
        var request = steveSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });

        var client = new HyperbahnClient({
            serviceName: 'hello-bob',
            callerName: 'hello-bob-test',
            hostPortList: cluster.hostPortList,
            tchannel: bob.channel,
            logger: DebugLogtron('hyperbahnClient')
        });

        client.once('advertised', onResponse);
        client.advertise();

        function onResponse() {
            thrift.send(request,
                'Hyperbahn::discover',
                null,
                {
                    query: {
                        serviceName: 'hello-bob'
                    }
                },
                check
            );
        }

        function check(err, res) {
            if (err) {
                assert.end(err);
            }
            assert.ok(res, 'should be a result');
            assert.ok(res.ok, 'result should be ok');
            assert.equals(covertHost(res.body.peers[0]), bob.channel.hostPort,
                'should get the expected hostPort');
            client.destroy();
            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: empty serviceName', {
        size: 5
    }, function t(cluster, assert) {
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        thrift.send(request,
            'Hyperbahn::discover',
            null,
            {
                query: {
                    serviceName: ''
                }
            },
            onResponse
        );
        function onResponse(err, res) {
            if (err) {
                assert.end(err);
            }
            assert.ok(!res.ok, 'should be not ok');
            assert.equals(res.body.message, 'invalid service name: ', 'error message as expected');
            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: an empty body', {
        size: 5
    }, function t(cluster, assert) {
        cluster.logger.whitelist('warn', 'Got unexpected invalid thrift for arg3');
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        var badSource = fs.readFileSync(path.join(__dirname, 'bad-hyperbahn-empty-req-body.thrift'), 'utf8');
        var badThrift = new TChannelAsThrift({source: badSource});
        badThrift.send(request,
            'Hyperbahn::discover',
            null,
            {},
            onResponse
        );
        function onResponse(err, res) {
            assert.ok(err, 'should be error');
            assert.equals(err.type, 'tchannel.bad-request', 'error should be bad request');
            assert.ok(err.message.indexOf(
                'tchannel-thrift-handler.parse-error.body-failed: Could not parse body (arg3) argument.\n' +
                'Expected Thrift encoded arg3 for endpoint Hyperbahn::discover.') === 0,
                'error message should be a parsing failure');
            assert.ok(err.message.indexOf(
                'Parsing error was: missing required field "query" with id 1 on discover_args') !== -1,
                'error message should be a parsing failure');

            var items = cluster.logger.items();
            assert.ok(items.length > 0 && items[0].msg === 'Got unexpected invalid thrift for arg3',
                'Do not miss the error log');

            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: a body with a query without the serviceName field', {
        size: 5
    }, function t(cluster, assert) {
        cluster.logger.whitelist('warn', 'Got unexpected invalid thrift for arg3');
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        var badSource = fs.readFileSync(path.join(__dirname, 'bad-hyperbahn-empty-req-body.thrift'), 'utf8');
        var badThrift = new TChannelAsThrift({source: badSource});
        badThrift.send(request,
            'Hyperbahn::discover',
            null,
            {query: {}},
            onResponse
        );
        function onResponse(err, res) {
            assert.ok(err, 'should be error');
            assert.equals(err.type, 'tchannel.bad-request', 'error should be bad request');
            assert.ok(err.message.indexOf(
                'tchannel-thrift-handler.parse-error.body-failed: Could not parse body (arg3) argument.\n' +
                'Expected Thrift encoded arg3 for endpoint Hyperbahn::discover.') === 0,
                'error message should be a parsing failure');
            assert.ok(err.message.indexOf(
                'Parsing error was: missing required field "query" with id 1 on discover_args') !== -1,
                'error message should be a parsing failure');

            var items = cluster.logger.items();
            assert.ok(items.length > 0 && items[0].msg === 'Got unexpected invalid thrift for arg3',
                'Do not miss the error log');

            assert.end();
        }
    });

    HyperbahnCluster.test('malformed thrift IDL: empty serviceName with no exception defined', {
        size: 5
    }, function t(cluster, assert) {
        cluster.logger.whitelist('warn', 'Got unexpected invalid thrift for arg3');
        var bob = cluster.remotes.bob;
        var bobSub = bob.channel.subChannels.hyperbahn;
        var request = bobSub.request({
            headers: {
                cn: 'test'
            },
            serviceName: 'hyperbahn',
            hasNoParent: true
        });
        var badSource = fs.readFileSync(path.join(__dirname, 'bad-hyperbahn-no-exception.thrift'), 'utf8');
        var badThrift = new TChannelAsThrift({source: badSource});
        badThrift.send(request,
            'Hyperbahn::discover',
            null,
            {
                query: {
                    serviceName: ''
                }
            },
            onResponse
        );
        function onResponse(err, res) {
            assert.ok(err, 'should be error');
            assert.equals(err.type, 'tchannel-thrift-handler.parse-error.body-failed',
                'error should be parser error');
            assert.ok(err.message.indexOf(
                'Could not parse body (arg3) argument.\n' +
                'Expected Thrift encoded arg3 for endpoint Hyperbahn::discover.') === 0,
                'error message should be a parsing failure');

            var items = cluster.logger.items();
            assert.ok(items.length > 0 && items[0].msg === 'Got unexpected invalid thrift for arg3',
                'Do not miss the error log');

            assert.end();
        }
    });
}
<|endoftext|>


<commit_before>/*
 * FastrSlowrPlayr
 *
 * Copyright (c) 2009 Kelvin Luck
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://www.kelvinluck.com/projects/fastrslowrplayr
 *
 * Depends:
 *   SWFObject
 *   FastrSlowrPlayr.swf
 */

var FastrSlowrPlayr = new function(element, settings)
{

	// used to hold our privateAPI closures...
	var players = [];

	// util method to ensure that settins are a cloned object (rather than a reference) and that
	// default settings are overridden with passed in settings...
	var cloneSettings = function(s)
	{
		var cloned = {};
		for (prop in FastrSlowrPlayr.defaults)
		{
			cloned[prop] = s[prop] || FastrSlowrPlayr.defaults[prop];
		}
		return cloned;
	};

	// public API
	return {
		/**
		* Initialises a FastrSlowrPlayr instance.
		*
		* @param settings Object An object containing a combination of the available options.
		* @option mp3File String The path to the mp3 file you want to load (absolute or relative to the .swf file).
		* @option swfPath String The path to the FastrSlowrPlayr.swf file (absolute or relative to this javascript file).
		* @option elementId String Specifies the id of the HTML element (containing your alternative content) you would
		* like to have replaced by your FastrSlowrPlayr.
		* @option autoplay Boolean Whether the mp3 should start playing automatically when it's loaded.
		* @option loop Boolean Whether the mp3 should loop once it's finished playing.
		* @option playbackSpeed Number The speed playback should be at. Any positive number is valid, 1 = normal speed.
		* @option volume Number The volume the mp3 should be played at - between 0 and 1.
		* @option pan Number How the mp3 should be panned. A number from -1 (fully left) to +1 (fully right). 0 is centrally panned.
		* @return Object A reference to the generated FastrSlowrPlayr. This instance has methods available to load, start, stop,
		* pause, etc the mp3
		**/
		init : function(settings)
		{
			var player = new function()
			{
				var id = players.length;
				var s = cloneSettings(settings);

				var flashvars = {
					id : id,
					playbackSpeed : s.playbackSpeed,
					loop : s.loop,
					volume : s.volume,
					pan : s.pan
				};

				var doOnLoad = [];

				var eventListeners = {};

				var dispatchEvent = function(type, args)
				{
					var listeners = eventListeners[type];
					if (listeners.length) {
						for (var i=0; i<listeners.length; i++) {
							listeners[i].apply(this, args);
						}
					}
				}

				// calls a method on the swf... If the swf isn't available yet then queues it to be done on load...
				var callOnSwf = function(func, arg)
				{
					if (swf) {
						if (arg != null) {
							swf[func](arg);
						} else {
							swf[func]();
						}
					} else {
						doOnLoad.push({func:func, arg:arg});
					}
				}

				swfobject.embedSWF(s.swfPath, s.elementId, "1", "1", "10.0.0", {}, flashvars);
				var swf;

				// create our private API for use from the fl* callbacks methods from flash...
				var privateAPI = {
					flOnReady : function()
					{
						swf = swfobject.getObjectById(s.elementId);
						// call any functions that were queued while we were loading...
						for (var i=0; i<doOnLoad.length; i++) {
							callOnSwf(doOnLoad[i].func, doOnLoad[i].arg);
						}
						// if an mp3 file was specified then we load it...
						if (s.mp3File) {
							swf.loadMp3(s.mp3File);
						}
					},
					flOnMP3Loaded : function()
					{
						dispatchEvent(FastrSlowrPlayr.EVENT_MP3_LOADED);
						if (s.autoplay) {
							swf.playMp3();
						}
					},
					flOnID3Available : function(id3Data)
					{
						dispatchEvent(FastrSlowrPlayr.EVENT_ID3_AVAILABLE, [id3Data]);
					}
				};
				// and add the private API to the array for later retrieval by id...
				players.push(privateAPI);

				// return our public API for use by
				return {
					id :				id,
					setVolume :			function(value)
										{
											s.volume = value;
											if (swf) {
												swf.setVolume(value);
											}
										},
					setPan :			function(value)
										{
											s.pan = value;
											if (swf) {
												swf.setPan(value);
											}
										},
					setAutoplay :		function(value)
										{
											s.autoplay = value;
										},
					setPlaybackSpeed :	function(value)
										{
											s.playbackSpeed = value;
											if (swf) {
												swf.setPlaybackSpeed(value);
											}
										},
					setPlayheadPosition : function(value)
										{
											callOnSwf('setPlayheadPosition', value);
										},
					getVolume :			function()
										{
											if (swf) {
												return swf.getVolume();
											}
											return s.volume;
										},
					getPan :			function()
										{
											if (swf) {
												return swf.getPan();
											}
											return s.pan;
										},
					getAutoplay :		function()
										{
											return s.autoplay;
										},
					getPlaybackSpeed :	function()
										{
											if (swf) {
												return swf.getPlaybackSpeed();
											}
											return s.playbackSpeed;
										},
					/**
					 * @return The position of the playhead as a percentage of the total length of
					 * the loaded mp3 (0 - 1).
					 */
					getPlayheadPosition : function()
										{
											if (swf) {
												return swf.getPlayheadPosition();
											}
											return 0;
										},
					load :				function(mp3File)
										{
											s.mp3File = mp3File;
											if (swf) {
												swf.loadMp3(mp3File);
											}
										},
					play :				function()
										{
											callOnSwf('playMp3', null);
										},
					pause :				function()
										{
											callOnSwf('pauseMp3', null);
										},
					stop :				function()
										{
											callOnSwf('stopMp3', null);
										},
					/**
					* Adds a listener for the given type of events.
					* The events that can be listened for are all defined by FastrSlowrPlayr.EVENT_* "constants"
					* @param eventName  String The type of event you want to listen to.
					* @param callback Function The function that you want to call when the event is fired.
					**/
					addEventListener :	function(eventName, callback)
										{
											if (!eventListeners[eventName]) {
												eventListeners[eventName] = [];
											}
											eventListeners[eventName].push(callback);
										},
					/**
					* Removes an event listener or all listeners for a given event type.
					* @param eventName String The typr of event you want to remove a listener for.
					* @param callback Function The specific listener you want to remove. If you don't pass this parameter
					* then all listeners for this event type will be removed.
					**/
					removeEventListener : function(eventName, callback)
										{
											if (callback == undefined) {
												eventListeners[eventName] = []
											} else {
												var registeredListeners = eventListeners[eventName];
												if (registeredListeners.length) {
													for (var i=0; i<registeredListeners.length; i++) {
														var listener = registeredListeners[i];
														if (listener == callback) {
															registeredListeners.splice(i, 1);
															// TODO: verify this actually works!
															break;
														}
													}
												}
											}
										}
				}
			};
			return player;
		},
		/**
		* Whether or not FastrSlowrPlayr is supported on this page (e.g. if the page has SWFObject loaded and
		* the user's browser has the correct version of the Flash Player available).
		* @return Boolean
		**/
		isAvailable : function()
		{
			return swfobject != null && swfobject.getFlashPlayerVersion().major > 9;
		},

		// The following properties are constants for each of the event names that can be listened too. This
		// is better than just using "magic strings" as it reduces the chances of mis-spelling etc...
		EVENT_ID3_AVAILABLE : 'id3Available',
		EVENT_MP3_LOADED : 'mp3Loaded',
		EVENT_MP3_COMPLETE : 'mp3Complete',

		// The following methods should not be called directly. They are used for callbacks from the flash file.
		// Unfortunately, because of the nature of the ExternalInterface callbacks from Flash we can't make these
		// methods private via code so instead we prefix them all with "fl" and document that they should never be
		// called directly.

		flOnReady : function(id)
		{
			players[id].flOnReady();
		},
		flOnMP3Loaded : function(id)
		{
			players[id].flOnMP3Loaded();
		},
		flOnID3Available : function(id, id3Data)
		{
			players[id].flOnID3Available(id3Data);
		}
	};
};

FastrSlowrPlayr.defaults = {
	volume:			1,
	pan:			0,
	autoplay:		false,
	playbackSpeed:	1,
	mp3File:		null,
	swfPath:		'../swf/FastrSlowrPlayr.swf',
	elementId:		'body',
	loop:			false
};
<commit_msg>Fix so it actually works when you set properties before the swf is ready...<commit_after>/*
 * FastrSlowrPlayr
 *
 * Copyright (c) 2009 Kelvin Luck
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://www.kelvinluck.com/projects/fastrslowrplayr
 *
 * Depends:
 *   SWFObject
 *   FastrSlowrPlayr.swf
 */

var FastrSlowrPlayr = new function(element, settings)
{

	// used to hold our privateAPI closures...
	var players = [];

	// util method to ensure that settins are a cloned object (rather than a reference) and that
	// default settings are overridden with passed in settings...
	var cloneSettings = function(s)
	{
		var cloned = {};
		for (prop in FastrSlowrPlayr.defaults)
		{
			cloned[prop] = s[prop] || FastrSlowrPlayr.defaults[prop];
		}
		return cloned;
	};

	// public API
	return {
		/**
		* Initialises a FastrSlowrPlayr instance.
		*
		* @param settings Object An object containing a combination of the available options.
		* @option mp3File String The path to the mp3 file you want to load (absolute or relative to the .swf file).
		* @option swfPath String The path to the FastrSlowrPlayr.swf file (absolute or relative to this javascript file).
		* @option elementId String Specifies the id of the HTML element (containing your alternative content) you would
		* like to have replaced by your FastrSlowrPlayr.
		* @option autoplay Boolean Whether the mp3 should start playing automatically when it's loaded.
		* @option loop Boolean Whether the mp3 should loop once it's finished playing.
		* @option playbackSpeed Number The speed playback should be at. Any positive number is valid, 1 = normal speed.
		* @option volume Number The volume the mp3 should be played at - between 0 and 1.
		* @option pan Number How the mp3 should be panned. A number from -1 (fully left) to +1 (fully right). 0 is centrally panned.
		* @return Object A reference to the generated FastrSlowrPlayr. This instance has methods available to load, start, stop,
		* pause, etc the mp3
		**/
		init : function(settings)
		{
			var player = new function()
			{
				var id = players.length;
				var s = cloneSettings(settings);

				var flashvars = {
					id : id,
					playbackSpeed : s.playbackSpeed,
					loop : s.loop,
					volume : s.volume,
					pan : s.pan
				};

				var doOnLoad = [];

				var eventListeners = {};

				var dispatchEvent = function(type, args)
				{
					var listeners = eventListeners[type];
					if (listeners.length) {
						for (var i=0; i<listeners.length; i++) {
							listeners[i].apply(this, args);
						}
					}
				}

				// calls a method on the swf... If the swf isn't available yet then queues it to be done on load...
				var callOnSwf = function(func, arg)
				{
					if (swf) {
						if (arg != null) {
							swf[func](arg);
						} else {
							swf[func]();
						}
					} else {
						doOnLoad.push({func:func, arg:arg});
					}
				}

				swfobject.embedSWF(s.swfPath, s.elementId, "1", "1", "10.0.0", {}, flashvars);
				var swf;

				// create our private API for use from the fl* callbacks methods from flash...
				var privateAPI = {
					flOnReady : function()
					{
						swf = swfobject.getObjectById(s.elementId);
						// call any functions that were queued while we were loading...
						for (var i=0; i<doOnLoad.length; i++) {
							callOnSwf(doOnLoad[i].func, doOnLoad[i].arg);
						}
						// if an mp3 file was specified then we load it...
						if (s.mp3File) {
							swf.loadMp3(s.mp3File);
						}
					},
					flOnMP3Loaded : function()
					{
						dispatchEvent(FastrSlowrPlayr.EVENT_MP3_LOADED);
						if (s.autoplay) {
							swf.playMp3();
						}
					},
					flOnID3Available : function(id3Data)
					{
						dispatchEvent(FastrSlowrPlayr.EVENT_ID3_AVAILABLE, [id3Data]);
					}
				};
				// and add the private API to the array for later retrieval by id...
				players.push(privateAPI);

				// return our public API for use by
				return {
					id :				id,
					setVolume :			function(value)
										{
											s.volume = value;
											callOnSwf('setVolume', value);
										},
					setPan :			function(value)
										{
											s.pan = value;
											callOnSwf('setPan', value);
										},
					setAutoplay :		function(value)
										{
											s.autoplay = value;
										},
					setPlaybackSpeed :	function(value)
										{
											s.playbackSpeed = value;
											callOnSwf('setPlaybackSpeed', value);
										},
					setPlayheadPosition : function(value)
										{
											callOnSwf('setPlayheadPosition', value);
										},
					getVolume :			function()
										{
											if (swf) {
												return swf.getVolume();
											}
											return s.volume;
										},
					getPan :			function()
										{
											if (swf) {
												return swf.getPan();
											}
											return s.pan;
										},
					getAutoplay :		function()
										{
											return s.autoplay;
										},
					getPlaybackSpeed :	function()
										{
											if (swf) {
												return swf.getPlaybackSpeed();
											}
											return s.playbackSpeed;
										},
					/**
					 * @return The position of the playhead as a percentage of the total length of
					 * the loaded mp3 (0 - 1).
					 */
					getPlayheadPosition : function()
										{
											if (swf) {
												return swf.getPlayheadPosition();
											}
											return 0;
										},
					load :				function(mp3File)
										{
											s.mp3File = mp3File;
											if (swf) {
												swf.loadMp3(mp3File);
											}
										},
					play :				function()
										{
											callOnSwf('playMp3', null);
										},
					pause :				function()
										{
											callOnSwf('pauseMp3', null);
										},
					stop :				function()
										{
											callOnSwf('stopMp3', null);
										},
					/**
					* Adds a listener for the given type of events.
					* The events that can be listened for are all defined by FastrSlowrPlayr.EVENT_* "constants"
					* @param eventName  String The type of event you want to listen to.
					* @param callback Function The function that you want to call when the event is fired.
					**/
					addEventListener :	function(eventName, callback)
										{
											if (!eventListeners[eventName]) {
												eventListeners[eventName] = [];
											}
											eventListeners[eventName].push(callback);
										},
					/**
					* Removes an event listener or all listeners for a given event type.
					* @param eventName String The typr of event you want to remove a listener for.
					* @param callback Function The specific listener you want to remove. If you don't pass this parameter
					* then all listeners for this event type will be removed.
					**/
					removeEventListener : function(eventName, callback)
										{
											if (callback == undefined) {
												eventListeners[eventName] = []
											} else {
												var registeredListeners = eventListeners[eventName];
												if (registeredListeners.length) {
													for (var i=0; i<registeredListeners.length; i++) {
														var listener = registeredListeners[i];
														if (listener == callback) {
															registeredListeners.splice(i, 1);
															// TODO: verify this actually works!
															break;
														}
													}
												}
											}
										}
				}
			};
			return player;
		},
		/**
		* Whether or not FastrSlowrPlayr is supported on this page (e.g. if the page has SWFObject loaded and
		* the user's browser has the correct version of the Flash Player available).
		* @return Boolean
		**/
		isAvailable : function()
		{
			return swfobject != null && swfobject.getFlashPlayerVersion().major > 9;
		},

		// The following properties are constants for each of the event names that can be listened too. This
		// is better than just using "magic strings" as it reduces the chances of mis-spelling etc...
		EVENT_ID3_AVAILABLE : 'id3Available',
		EVENT_MP3_LOADED : 'mp3Loaded',
		EVENT_MP3_COMPLETE : 'mp3Complete',

		// The following methods should not be called directly. They are used for callbacks from the flash file.
		// Unfortunately, because of the nature of the ExternalInterface callbacks from Flash we can't make these
		// methods private via code so instead we prefix them all with "fl" and document that they should never be
		// called directly.

		flOnReady : function(id)
		{
			players[id].flOnReady();
		},
		flOnMP3Loaded : function(id)
		{
			players[id].flOnMP3Loaded();
		},
		flOnID3Available : function(id, id3Data)
		{
			players[id].flOnID3Available(id3Data);
		}
	};
};

FastrSlowrPlayr.defaults = {
	volume:			1,
	pan:			0,
	autoplay:		false,
	playbackSpeed:	1,
	mp3File:		null,
	swfPath:		'../swf/FastrSlowrPlayr.swf',
	elementId:		'body',
	loop:			false
};
<|endoftext|>