<commit_before>/*
 * node-rdkafka - Node.js wrapper for RdKafka C/C++ library
 *
 * Copyright (c) 2016 Blizzard Entertainment
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE.txt file for details.
 */

module.exports = Client;

var Emitter = require('events').EventEmitter;
var util = require('util');
var Kafka = require('../librdkafka.js');
var assert = require('assert');

var LibrdKafkaError = require('./error');

util.inherits(Client, Emitter);

/**
 * Base class for Consumer and Producer
 *
 * This should not be created independently, but rather is
 * the base class on which both producer and consumer
 * get their common functionality.
 *
 * @param {object} globalConf - Global configuration in key value pairs.
 * @param {function} SubClientType - The function representing the subclient
 * type. In C++ land this needs to be a class that inherits from Connection.
 * @param {object} topicConf - Topic configuration in key value pairs
 * @constructor
 * @extends Emitter
 */
function Client(globalConf, SubClientType, topicConf) {
  if (!(this instanceof Client)) {
    return new Client(globalConf, SubClientType, topicConf);
  }

  Emitter.call(this);

  // This superclass must be initialized with the Kafka.{Producer,Consumer}
  // @example var client = new Client({}, Kafka.Producer);
  // remember this is a superclass so this will get taken care of in
  // the producer and consumer main wrappers

  var no_event_cb = globalConf.event_cb === false;
  topicConf = topicConf || {};

  // delete this because librdkafka will complain since this particular
  // key is a real conf value
  delete globalConf.event_cb;

  this._client = new SubClientType(globalConf, topicConf);

  // primitive callbacks from c++ wrapper
  // need binds on them if we want to broadcast the event via the emitter

  // However, the C++ wrapper lazy processes callbacks.
  // If we want to improve performance, removing this line is a good way to do it.
  // we need to do this on creation of the object
  // thats why we did some processing on config

  // Self required because we are inside an async callback function scope
  if (!no_event_cb) {
    this._client.onEvent(function eventHandler(eventType, eventData) {
      switch (eventType) {
        case 'error':
          self.emit('event.error', LibrdKafkaError.create(eventData));
          break;
        case 'stats':
          self.emit('event.stats', eventData);
          break;
        case 'log':
          self.emit('event.log', eventData);
          break;
        default:
          self.emit('event.event', eventData);
          self.emit('event.' + eventType, eventData);
      }
    });
  }

  this.metrics = {};
  this._isConnected = false;
  this.errorCounter = 0;

  /**
   * Metadata object. Starts out empty but will be filled with information after
   * the initial connect.
   *
   * @type {Client~Metadata}
   */
  this._metadata = {};

  var self = this;

  this.on('ready', function(info) {
    self.metrics.connectionOpened = Date.now();
    self.name = info.name;
  })
  .on('disconnected', function() {
    // reset metrics
    self.metrics = {};
    self._isConnected = false;
    // keep the metadata. it still may be useful
  })
  .on('event.error', function(err) {
    self.lastError = err;
    ++self.errorCounter;
  });

}

/**
 * Connect to the broker and receive its metadata.
 *
 * Connects to a broker by establishing the client and fetches its metadata.
 *
 * @param {object} metadataOptions - Options to be sent to the metadata.
 * @param {string} metadataOptions.topic - Topic to fetch metadata for. Empty string is treated as empty.
 * @param {boolean} metadataOptions.allTopics - Fetch metadata for all topics, not just the ones we know about.
 * @param {int} metadataOptions.timeout - The timeout, in ms, to allow for fetching metadata. Defaults to 30000ms
 * @param  {Client~connectionCallback} cb - Callback that indicates we are
 * done connecting.
 * @return {Client} - Returns itself.
 */
Client.prototype.connect = function(metadataOptions, cb) {
  var self = this;

  var next = function(err, data) {
    self._isConnecting = false;
    if (cb) {
      cb(err, data);
    }
  };

  if (this._isConnected) {
    setImmediate(next);
    return self;
  }

  if (this._isConnecting) {
    this.once('ready', function() {
      next(null, this._metadata);
    });
    return self;
  }

  this._isConnecting = true;

  var fail = function(err) {
    var callbackCalled = false;
    var t;

    if (self._isConnected) {
      self._isConnected = false;
      self._client.disconnect(function() {
        if (callbackCalled) {
          return;
        }
        clearTimeout(t);
        callbackCalled = true;

        next(err); return;
      });

      // don't take too long. this is a failure, after all
      t = setTimeout(function() {
        if (callbackCalled) {
          return;
        }
        callbackCalled = true;

        next(err); return;
      }, 10000).unref();

      self.emit('connection.failure', self.metrics);
    } else {

      next(err);
    }
  };

  this._client.connect(function(err, info) {
    if (err) {
      fail(LibrdKafkaError.create(err)); return;
    }

    self._isConnected = true;

    // Otherwise we are successful
    self.getMetadata(metadataOptions || {}, function(err, metadata) {
      if (err) {
        // We are connected so we need to disconnect
        fail(LibrdKafkaError.create(err)); return;
      }

      self._isConnecting = false;
      // We got the metadata otherwise. It is set according to above param
      // Set it here as well so subsequent ready callbacks
      // can check it
      self._isConnected = true;

      /**
       * Ready event. Called when the Client connects successfully
       *
       * @event Client#ready
       * @type {object}
       * @property {string} name - the name of the broker.
       */
      self.emit('ready', info, metadata);
      next(null, metadata); return;

    });

  });

  return self;

};

/**
 * Get the native Kafka client.
 *
 * You probably shouldn't use this, but if you want to execute methods directly
 * on the c++ wrapper you can do it here.
 *
 * @see connection.cc
 * @return {Connection} - The native Kafka client.
 */
Client.prototype.getClient = function() {
  return this._client;
};

/**
 * Find out how long we have been connected to Kafka.
 *
 * @return {number} - Milliseconds since the connection has been established.
 */
Client.prototype.connectedTime = function() {
  if (!this.isConnected()) {
    return 0;
  }
  return Date.now() - this.metrics.connectionOpened;
};

/**
 * Whether or not we are connected to Kafka.
 *
 * @return {boolean} - Whether we are connected.
 */
Client.prototype.isConnected = function() {
  return !!(this._isConnected && this._client);
};

/**
 * Get the last error emitted if it exists.
 *
 * @return {LibrdKafkaError} - Returns the LibrdKafkaError or null if
 * one hasn't been thrown.
 */
Client.prototype.getLastError = function() {
  return this.lastError || null;
};

/**
 * Disconnect from the Kafka client.
 *
 * This method will disconnect us from Kafka unless we are already in a
 * disconnecting state. Use this when you're done reading or producing messages
 * on a given client.
 *
 * It will also emit the disconnected event.
 *
 * @fires Client#disconnected
 * @return {function} - Callback to call when disconnection is complete.
 */
Client.prototype.disconnect = function(cb) {
  var self = this;

  if (!this._isDisconnecting && this._client) {
    this._isDisconnecting = true;
    this._client.disconnect(function() {
      // this take 5000 milliseconds. Librdkafka needs to make sure the memory
      // has been cleaned up before we delete things. @see RdKafka::wait_destroyed

      // Broadcast metrics. Gives people one last chance to do something with them
      self._isDisconnecting = false;
      /**
       * Disconnect event. Called after disconnection is finished.
       *
       * @event Client#disconnected
       * @type {object}
       * @property {date} connectionOpened - when the connection was opened.
       */
      self.emit('disconnected', self.metrics);
      if (cb) {
        cb(null, self.metrics);
      }

    });

  }

  return self;
};

/**
 * Get client metadata.
 *
 * Note: using a metadataOptions.topic parameter has a potential side-effect.
 * A Topic object will be created, if it did not exist yet, with default options
 * and it will be cached by librdkafka.
 *
 * A subsequent call to create the topic object with specific options (e.g. acks) will return
 * the previous instance and the specific options will be silently ignored.
 *
 * To avoid this side effect, the topic object can be created with the expected options before requesting metadata,
 * or the metadata request can be performed for all topics (by omitting metadataOptions.topic).
 *
 * @param {object} metadataOptions - Metadata options to pass to the client.
 * @param {string} metadataOptions.topic - Topic string for which to fetch
 * metadata
 * @param {number} metadataOptions.timeout - Max time, in ms, to try to fetch
 * metadata before timing out. Defaults to 3000.
 * @param {Client~metadataCallback} cb - Callback to fire with the metadata.
 */
Client.prototype.getMetadata = function(metadataOptions, cb) {
  if (!this.isConnected()) {
    return cb(new Error('Client is disconnected'));
  }

  var self = this;

  this._client.getMetadata(metadataOptions || {}, function(err, metadata) {
    if (err) {
      if (cb) {
        cb(LibrdKafkaError.create(err));
      }
      return;
    }

    // No error otherwise
    self._metadata = metadata;

    if (cb) {
      cb(null, metadata);
    }

  });

};

/**
 * Query offsets from the broker.
 *
 * This function makes a call to the broker to get the current low (oldest/beginning)
 * and high (newest/end) offsets for a topic partition.
 *
 * @param {string} topic - Topic to recieve offsets from.
 * @param {number} partition - Partition of the provided topic to recieve offsets from
 * @param {number} timeout - Number of ms to wait to recieve a response.
 * @param {Client~watermarkOffsetsCallback} cb - Callback to fire with the offsets.
 */
Client.prototype.queryWatermarkOffsets = function(topic, partition, timeout, cb) {
  if (!this.isConnected()) {
    if (cb) {
      return cb(new Error('Client is disconnected'));
    } else {
      return;
    }
  }

  var self = this;

  if (typeof timeout === 'function') {
    cb = timeout;
    timeout = 1000;
  }

  if (!timeout) {
    timeout = 1000;
  }

  this._client.queryWatermarkOffsets(topic, partition, timeout, function(err, offsets) {
    if (err) {
      if (cb) {
        cb(LibrdKafkaError.create(err));
      }
      return;
    }

    if (cb) {
      cb(null, offsets);
    }

  });
};

/**
 * Query offsets for times from the broker.
 *
 * This function makes a call to the broker to get the offsets for times specified.
 *
 * @param {TopicPartition[]} toppars - Array of topic partitions. The offset in these
 *                                     should instead refer to a timestamp you want
 *                                     offsets for
 * @param {number} timeout - Number of ms to wait to recieve a response.
 * @param {Client~offsetsForTimesCallback} cb - Callback to fire with the filled in offsets.
 */
Client.prototype.offsetsForTimes = function(toppars, timeout, cb) {
  if (!this.isConnected()) {
    if (cb) {
      return cb(new Error('Client is disconnected'));
    } else {
      return;
    }
  }

  var self = this;

  if (typeof timeout === 'function') {
    cb = timeout;
    timeout = 1000;
  }

  if (!timeout) {
    timeout = 1000;
  }

  this._client.offsetsForTimes(toppars, timeout, function(err, toppars) {
    if (err) {
      if (cb) {
        cb(LibrdKafkaError.create(err));
      }
      return;
    }

    if (cb) {
      cb(null, toppars);
    }

  });
};

/**
 * Wrap a potential RdKafka error.
 *
 * This internal method is meant to take a return value
 * from a function that returns an RdKafka error code, throw if it
 * is an error (Making it a proper librdkafka error object), or
 * return the appropriate value otherwise.
 *
 * It is intended to be used in a return statement,
 *
 * @private
 * @param {number} errorCode - Error code returned from a native method
 * @param {bool} intIsError - If specified true, any non-number return type will be classified as a success
 * @return {boolean} - Returns true or the method return value unless it throws.
 */
Client.prototype._errorWrap = function(errorCode, intIsError) {
  var returnValue = true;
  if (intIsError) {
    returnValue = errorCode;
    errorCode = typeof errorCode === 'number' ? errorCode : 0;
  }

  if (errorCode !== LibrdKafkaError.codes.ERR_NO_ERROR) {
    var e = LibrdKafkaError.create(errorCode);
    throw e;
  }

  return returnValue;
};

/**
 * This callback is used to pass metadata or an error after a successful
 * connection
 *
 * @callback Client~connectionCallback
 * @param {Error} err - An error, if one occurred while connecting.
 * @param {Client~Metadata} metadata - Metadata object.
 */

 /**
  * This callback is used to pass offsets or an error after a successful
  * query
  *
  * @callback Client~watermarkOffsetsCallback
  * @param {Error} err - An error, if one occurred while connecting.
  * @param {Client~watermarkOffsets} offsets - Watermark offsets
  */

  /**
   * This callback is used to pass toppars or an error after a successful
   * times query
   *
   * @callback Client~offsetsForTimesCallback
   * @param {Error} err - An error, if one occurred while connecting.
   * @param {TopicPartition[]} toppars - Topic partitions with offsets filled in
   */

/**
 * @typedef {object} Client~watermarkOffsets
 * @property {number} high - High (newest/end) offset
 * @property {number} low - Low (oldest/beginning) offset
 */

/**
 * @typedef {object} Client~MetadataBroker
 * @property {number} id - Broker ID
 * @property {string} host - Broker host
 * @property {number} port - Broker port.
 */

/**
 * @typedef {object} Client~MetadataTopic
 * @property {string} name - Topic name
 * @property {Client~MetadataPartition[]} partitions - Array of partitions
 */

/**
 * @typedef {object} Client~MetadataPartition
 * @property {number} id - Partition id
 * @property {number} leader - Broker ID for the partition leader
 * @property {number[]} replicas - Array of replica IDs
 * @property {number[]} isrs - Arrqay of ISRS ids
*/

/**
 * Metadata object.
 *
 * This is the representation of Kafka metadata in JavaScript.
 *
 * @typedef {object} Client~Metadata
 * @property {number} orig_broker_id - The broker ID of the original bootstrap
 * broker.
 * @property {string} orig_broker_name - The name of the original bootstrap
 * broker.
 * @property {Client~MetadataBroker[]} brokers - An array of broker objects
 * @property {Client~MetadataTopic[]} topics - An array of topics.
 */
<commit_msg>connection.failure error emit<commit_after>/*
 * node-rdkafka - Node.js wrapper for RdKafka C/C++ library
 *
 * Copyright (c) 2016 Blizzard Entertainment
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE.txt file for details.
 */

module.exports = Client;

var Emitter = require('events').EventEmitter;
var util = require('util');
var Kafka = require('../librdkafka.js');
var assert = require('assert');

var LibrdKafkaError = require('./error');

util.inherits(Client, Emitter);

/**
 * Base class for Consumer and Producer
 *
 * This should not be created independently, but rather is
 * the base class on which both producer and consumer
 * get their common functionality.
 *
 * @param {object} globalConf - Global configuration in key value pairs.
 * @param {function} SubClientType - The function representing the subclient
 * type. In C++ land this needs to be a class that inherits from Connection.
 * @param {object} topicConf - Topic configuration in key value pairs
 * @constructor
 * @extends Emitter
 */
function Client(globalConf, SubClientType, topicConf) {
  if (!(this instanceof Client)) {
    return new Client(globalConf, SubClientType, topicConf);
  }

  Emitter.call(this);

  // This superclass must be initialized with the Kafka.{Producer,Consumer}
  // @example var client = new Client({}, Kafka.Producer);
  // remember this is a superclass so this will get taken care of in
  // the producer and consumer main wrappers

  var no_event_cb = globalConf.event_cb === false;
  topicConf = topicConf || {};

  // delete this because librdkafka will complain since this particular
  // key is a real conf value
  delete globalConf.event_cb;

  this._client = new SubClientType(globalConf, topicConf);

  // primitive callbacks from c++ wrapper
  // need binds on them if we want to broadcast the event via the emitter

  // However, the C++ wrapper lazy processes callbacks.
  // If we want to improve performance, removing this line is a good way to do it.
  // we need to do this on creation of the object
  // thats why we did some processing on config

  // Self required because we are inside an async callback function scope
  if (!no_event_cb) {
    this._client.onEvent(function eventHandler(eventType, eventData) {
      switch (eventType) {
        case 'error':
          self.emit('event.error', LibrdKafkaError.create(eventData));
          break;
        case 'stats':
          self.emit('event.stats', eventData);
          break;
        case 'log':
          self.emit('event.log', eventData);
          break;
        default:
          self.emit('event.event', eventData);
          self.emit('event.' + eventType, eventData);
      }
    });
  }

  this.metrics = {};
  this._isConnected = false;
  this.errorCounter = 0;

  /**
   * Metadata object. Starts out empty but will be filled with information after
   * the initial connect.
   *
   * @type {Client~Metadata}
   */
  this._metadata = {};

  var self = this;

  this.on('ready', function(info) {
    self.metrics.connectionOpened = Date.now();
    self.name = info.name;
  })
  .on('disconnected', function() {
    // reset metrics
    self.metrics = {};
    self._isConnected = false;
    // keep the metadata. it still may be useful
  })
  .on('event.error', function(err) {
    self.lastError = err;
    ++self.errorCounter;
  });

}

/**
 * Connect to the broker and receive its metadata.
 *
 * Connects to a broker by establishing the client and fetches its metadata.
 *
 * @param {object} metadataOptions - Options to be sent to the metadata.
 * @param {string} metadataOptions.topic - Topic to fetch metadata for. Empty string is treated as empty.
 * @param {boolean} metadataOptions.allTopics - Fetch metadata for all topics, not just the ones we know about.
 * @param {int} metadataOptions.timeout - The timeout, in ms, to allow for fetching metadata. Defaults to 30000ms
 * @param  {Client~connectionCallback} cb - Callback that indicates we are
 * done connecting.
 * @return {Client} - Returns itself.
 */
Client.prototype.connect = function(metadataOptions, cb) {
  var self = this;

  var next = function(err, data) {
    self._isConnecting = false;
    if (cb) {
      cb(err, data);
    }
  };

  if (this._isConnected) {
    setImmediate(next);
    return self;
  }

  if (this._isConnecting) {
    this.once('ready', function() {
      next(null, this._metadata);
    });
    return self;
  }

  this._isConnecting = true;

  var fail = function(err) {
    var callbackCalled = false;
    var t;

    if (self._isConnected) {
      self._isConnected = false;
      self._client.disconnect(function() {
        if (callbackCalled) {
          return;
        }
        clearTimeout(t);
        callbackCalled = true;

        next(err); return;
      });

      // don't take too long. this is a failure, after all
      t = setTimeout(function() {
        if (callbackCalled) {
          return;
        }
        callbackCalled = true;

        next(err); return;
      }, 10000).unref();

      self.emit('connection.failure', err, self.metrics);
    } else {

      next(err);
    }
  };

  this._client.connect(function(err, info) {
    if (err) {
      fail(LibrdKafkaError.create(err)); return;
    }

    self._isConnected = true;

    // Otherwise we are successful
    self.getMetadata(metadataOptions || {}, function(err, metadata) {
      if (err) {
        // We are connected so we need to disconnect
        fail(LibrdKafkaError.create(err)); return;
      }

      self._isConnecting = false;
      // We got the metadata otherwise. It is set according to above param
      // Set it here as well so subsequent ready callbacks
      // can check it
      self._isConnected = true;

      /**
       * Ready event. Called when the Client connects successfully
       *
       * @event Client#ready
       * @type {object}
       * @property {string} name - the name of the broker.
       */
      self.emit('ready', info, metadata);
      next(null, metadata); return;

    });

  });

  return self;

};

/**
 * Get the native Kafka client.
 *
 * You probably shouldn't use this, but if you want to execute methods directly
 * on the c++ wrapper you can do it here.
 *
 * @see connection.cc
 * @return {Connection} - The native Kafka client.
 */
Client.prototype.getClient = function() {
  return this._client;
};

/**
 * Find out how long we have been connected to Kafka.
 *
 * @return {number} - Milliseconds since the connection has been established.
 */
Client.prototype.connectedTime = function() {
  if (!this.isConnected()) {
    return 0;
  }
  return Date.now() - this.metrics.connectionOpened;
};

/**
 * Whether or not we are connected to Kafka.
 *
 * @return {boolean} - Whether we are connected.
 */
Client.prototype.isConnected = function() {
  return !!(this._isConnected && this._client);
};

/**
 * Get the last error emitted if it exists.
 *
 * @return {LibrdKafkaError} - Returns the LibrdKafkaError or null if
 * one hasn't been thrown.
 */
Client.prototype.getLastError = function() {
  return this.lastError || null;
};

/**
 * Disconnect from the Kafka client.
 *
 * This method will disconnect us from Kafka unless we are already in a
 * disconnecting state. Use this when you're done reading or producing messages
 * on a given client.
 *
 * It will also emit the disconnected event.
 *
 * @fires Client#disconnected
 * @return {function} - Callback to call when disconnection is complete.
 */
Client.prototype.disconnect = function(cb) {
  var self = this;

  if (!this._isDisconnecting && this._client) {
    this._isDisconnecting = true;
    this._client.disconnect(function() {
      // this take 5000 milliseconds. Librdkafka needs to make sure the memory
      // has been cleaned up before we delete things. @see RdKafka::wait_destroyed

      // Broadcast metrics. Gives people one last chance to do something with them
      self._isDisconnecting = false;
      /**
       * Disconnect event. Called after disconnection is finished.
       *
       * @event Client#disconnected
       * @type {object}
       * @property {date} connectionOpened - when the connection was opened.
       */
      self.emit('disconnected', self.metrics);
      if (cb) {
        cb(null, self.metrics);
      }

    });

  }

  return self;
};

/**
 * Get client metadata.
 *
 * Note: using a metadataOptions.topic parameter has a potential side-effect.
 * A Topic object will be created, if it did not exist yet, with default options
 * and it will be cached by librdkafka.
 *
 * A subsequent call to create the topic object with specific options (e.g. acks) will return
 * the previous instance and the specific options will be silently ignored.
 *
 * To avoid this side effect, the topic object can be created with the expected options before requesting metadata,
 * or the metadata request can be performed for all topics (by omitting metadataOptions.topic).
 *
 * @param {object} metadataOptions - Metadata options to pass to the client.
 * @param {string} metadataOptions.topic - Topic string for which to fetch
 * metadata
 * @param {number} metadataOptions.timeout - Max time, in ms, to try to fetch
 * metadata before timing out. Defaults to 3000.
 * @param {Client~metadataCallback} cb - Callback to fire with the metadata.
 */
Client.prototype.getMetadata = function(metadataOptions, cb) {
  if (!this.isConnected()) {
    return cb(new Error('Client is disconnected'));
  }

  var self = this;

  this._client.getMetadata(metadataOptions || {}, function(err, metadata) {
    if (err) {
      if (cb) {
        cb(LibrdKafkaError.create(err));
      }
      return;
    }

    // No error otherwise
    self._metadata = metadata;

    if (cb) {
      cb(null, metadata);
    }

  });

};

/**
 * Query offsets from the broker.
 *
 * This function makes a call to the broker to get the current low (oldest/beginning)
 * and high (newest/end) offsets for a topic partition.
 *
 * @param {string} topic - Topic to recieve offsets from.
 * @param {number} partition - Partition of the provided topic to recieve offsets from
 * @param {number} timeout - Number of ms to wait to recieve a response.
 * @param {Client~watermarkOffsetsCallback} cb - Callback to fire with the offsets.
 */
Client.prototype.queryWatermarkOffsets = function(topic, partition, timeout, cb) {
  if (!this.isConnected()) {
    if (cb) {
      return cb(new Error('Client is disconnected'));
    } else {
      return;
    }
  }

  var self = this;

  if (typeof timeout === 'function') {
    cb = timeout;
    timeout = 1000;
  }

  if (!timeout) {
    timeout = 1000;
  }

  this._client.queryWatermarkOffsets(topic, partition, timeout, function(err, offsets) {
    if (err) {
      if (cb) {
        cb(LibrdKafkaError.create(err));
      }
      return;
    }

    if (cb) {
      cb(null, offsets);
    }

  });
};

/**
 * Query offsets for times from the broker.
 *
 * This function makes a call to the broker to get the offsets for times specified.
 *
 * @param {TopicPartition[]} toppars - Array of topic partitions. The offset in these
 *                                     should instead refer to a timestamp you want
 *                                     offsets for
 * @param {number} timeout - Number of ms to wait to recieve a response.
 * @param {Client~offsetsForTimesCallback} cb - Callback to fire with the filled in offsets.
 */
Client.prototype.offsetsForTimes = function(toppars, timeout, cb) {
  if (!this.isConnected()) {
    if (cb) {
      return cb(new Error('Client is disconnected'));
    } else {
      return;
    }
  }

  var self = this;

  if (typeof timeout === 'function') {
    cb = timeout;
    timeout = 1000;
  }

  if (!timeout) {
    timeout = 1000;
  }

  this._client.offsetsForTimes(toppars, timeout, function(err, toppars) {
    if (err) {
      if (cb) {
        cb(LibrdKafkaError.create(err));
      }
      return;
    }

    if (cb) {
      cb(null, toppars);
    }

  });
};

/**
 * Wrap a potential RdKafka error.
 *
 * This internal method is meant to take a return value
 * from a function that returns an RdKafka error code, throw if it
 * is an error (Making it a proper librdkafka error object), or
 * return the appropriate value otherwise.
 *
 * It is intended to be used in a return statement,
 *
 * @private
 * @param {number} errorCode - Error code returned from a native method
 * @param {bool} intIsError - If specified true, any non-number return type will be classified as a success
 * @return {boolean} - Returns true or the method return value unless it throws.
 */
Client.prototype._errorWrap = function(errorCode, intIsError) {
  var returnValue = true;
  if (intIsError) {
    returnValue = errorCode;
    errorCode = typeof errorCode === 'number' ? errorCode : 0;
  }

  if (errorCode !== LibrdKafkaError.codes.ERR_NO_ERROR) {
    var e = LibrdKafkaError.create(errorCode);
    throw e;
  }

  return returnValue;
};

/**
 * This callback is used to pass metadata or an error after a successful
 * connection
 *
 * @callback Client~connectionCallback
 * @param {Error} err - An error, if one occurred while connecting.
 * @param {Client~Metadata} metadata - Metadata object.
 */

 /**
  * This callback is used to pass offsets or an error after a successful
  * query
  *
  * @callback Client~watermarkOffsetsCallback
  * @param {Error} err - An error, if one occurred while connecting.
  * @param {Client~watermarkOffsets} offsets - Watermark offsets
  */

  /**
   * This callback is used to pass toppars or an error after a successful
   * times query
   *
   * @callback Client~offsetsForTimesCallback
   * @param {Error} err - An error, if one occurred while connecting.
   * @param {TopicPartition[]} toppars - Topic partitions with offsets filled in
   */

/**
 * @typedef {object} Client~watermarkOffsets
 * @property {number} high - High (newest/end) offset
 * @property {number} low - Low (oldest/beginning) offset
 */

/**
 * @typedef {object} Client~MetadataBroker
 * @property {number} id - Broker ID
 * @property {string} host - Broker host
 * @property {number} port - Broker port.
 */

/**
 * @typedef {object} Client~MetadataTopic
 * @property {string} name - Topic name
 * @property {Client~MetadataPartition[]} partitions - Array of partitions
 */

/**
 * @typedef {object} Client~MetadataPartition
 * @property {number} id - Partition id
 * @property {number} leader - Broker ID for the partition leader
 * @property {number[]} replicas - Array of replica IDs
 * @property {number[]} isrs - Arrqay of ISRS ids
*/

/**
 * Metadata object.
 *
 * This is the representation of Kafka metadata in JavaScript.
 *
 * @typedef {object} Client~Metadata
 * @property {number} orig_broker_id - The broker ID of the original bootstrap
 * broker.
 * @property {string} orig_broker_name - The name of the original bootstrap
 * broker.
 * @property {Client~MetadataBroker[]} brokers - An array of broker objects
 * @property {Client~MetadataTopic[]} topics - An array of topics.
 */
<|endoftext|>


<commit_before>import path from 'path';
import { isAbsoluteURL } from './misc/utils';

export default {
  caches: 'all',
  publicPath: void 0,
  updateStrategy: 'changed',
  responseStrategy: 'cache-first',
  externals: [],
  excludes: ['**/.*', '**/*.map'],
  // Hack to have intermediate value, e.g. default one, true and false
  relativePaths: ':relativePaths:',
  version: null,
  autoUpdate: false,

  rewrites(asset) {
    return asset.replace(/^([\s\S]*?)index.htm(l?)$/, (match, dir) => {
      if (isAbsoluteURL(match)) {
        return match;
      }

      return dir || './';
    });
  },

  cacheMaps: null,

  ServiceWorker: {
    output: 'sw.js',
    entry: path.join(__dirname, '../tpls/empty-entry.js'),
    scope: null,
    events: false,
    prefetchRequest: {
      credentials: 'omit',
      headers: void 0,
      mode: 'cors',
      cache: void 0
    },
    minify: null,

    navigateFallbackURL: void 0,
    navigateFallbackForRedirects: false
  },

  AppCache: {
    NETWORK: '*',
    FALLBACK: null,
    directory: 'appcache/',
    caches: ['main'],
    events: false,
    disableInstall: false,
    includeCrossOrigin: false
  },

  // Needed for testing
  __tests: {
    swMetadataOnly: false,
    ignoreRuntime: false,
    noVersionDump: false
  },

  // Not yet used
  alwaysRevalidate: void 0,
  preferOnline: void 0,
  ignoreSearch: ['**'],
};<commit_msg>Remove unused options<commit_after>import path from 'path';
import { isAbsoluteURL } from './misc/utils';

export default {
  caches: 'all',
  publicPath: void 0,
  updateStrategy: 'changed',
  responseStrategy: 'cache-first',
  externals: [],
  excludes: ['**/.*', '**/*.map'],
  // Hack to have intermediate value, e.g. default one, true and false
  relativePaths: ':relativePaths:',
  version: null,
  autoUpdate: false,

  rewrites(asset) {
    return asset.replace(/^([\s\S]*?)index.htm(l?)$/, (match, dir) => {
      if (isAbsoluteURL(match)) {
        return match;
      }

      return dir || './';
    });
  },

  cacheMaps: null,

  ServiceWorker: {
    output: 'sw.js',
    entry: path.join(__dirname, '../tpls/empty-entry.js'),
    scope: null,
    events: false,
    prefetchRequest: {
      credentials: 'omit',
      headers: void 0,
      mode: 'cors',
      cache: void 0
    },
    minify: null,

    navigateFallbackURL: void 0,
    navigateFallbackForRedirects: false
  },

  AppCache: {
    NETWORK: '*',
    FALLBACK: null,
    directory: 'appcache/',
    caches: ['main'],
    events: false,
    disableInstall: false,
    includeCrossOrigin: false
  },

  // Needed for testing
  __tests: {
    swMetadataOnly: false,
    ignoreRuntime: false,
    noVersionDump: false
  }
};<|endoftext|>


<commit_before>var app = require('cantina')
  , idgen = require('idgen')
  , nested = require('nested-objects')
  , _ = require('underscore');

// Note that unlike modeler, we do not guarantee that you can list documents
// in insertion order

module.exports = function (_opts) {
  var opts = copy(_opts);
  if (!opts.db) throw new Error('must pass a node-mongodb-native db with options.db');
  if (!opts.name) throw new Error('must pass a collection name with options.name');

  // We promise these will always* be in the model
  // *updated is only there if the model have been saved
  var requiredProperties = [ 'id', 'rev', 'created', 'updated' ];

  var store = {}
    , db = opts.db
    , collection = db.collection(opts.name)
    , privateProperties = (opts.privateProperties && opts.privateProperties.filter(function (prop) {
      return requiredProperties.indexOf(prop) === -1;
    })) || [];

  Object.keys(collection.constructor.prototype).forEach(function (prop) {
    if (typeof this[prop] === 'function') store['_' + prop] = this[prop].bind(collection);
    else store['_' + prop] = collection[prop];
  }, collection.constructor.prototype);

  // Takes a field projection: http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/#read-operations-projection
  // and returns a projection combined with the store's default private properties
  // Performs minimal validation on the passed fields, so if you pass in an invalid
  // projection (e.g., { foo: 1, bar: 0 }) you will get unpredictable results.
  function getFieldsProjection (fields) {
    var projection = (fields && copy(fields)) || {}
      , include;
    Object.keys(projection).forEach(function (field) {
      var flag = +projection[field];
      if (typeof include === 'undefined') include = flag;
      // can't mix and match flags, except for special case exluding _id
      else if (include !== flag && field !== '_id' && include !== 1 && flag !== 0) delete projection[field];
    });
    if (include !== 1) {
      privateProperties.forEach(function (field) {
        projection[field] = 0;
      });
      if (include === 0) {
        requiredProperties.forEach(function (field) {
          if (field in projection) delete projection[field];
        });
      }
    }
    else {
      // No. We really don't allow private properties to be in the results.
      privateProperties.forEach(function (field) {
        if (field in projection) delete projection[field];
      });
      requiredProperties.forEach(function (field) {
        projection[field] = 1;
      });
    }
    return Object.keys(projection).length ? projection : null;
  }

  // Ensure indexes exist
  // No other operations can happen while indexes are being created, so we
  // run this on initialize and only really care if there's an error
  collection.ensureIndex([{ "id": 1, "rev": 1 }], { unique: true }, function (err) {
    if (err) app.emit('error', err);
  });

  /**
   * @param {Object} query (optional)
   * @param {Object} options (optional, required if query is provided)
   * @param {Function} cb
   */
  store.list = function (query, options, cb) {
    if (arguments.length < 3) {
      if (typeof options === 'function') {
        cb = options;
        options = query;
        query = {};
      }
      else if (typeof query === 'function') {
        cb = query;
        options = {};
        query = {};
      }
    }
    options = _.defaults(options || {}, {
      sort: '_id',
      offset: 0,
      reverse: true
    });

    // translate modeler keys to mongo keys
    var queryOptions = {}, sort = {};
    sort[options.sort] = options.reverse ? -1 : 1;
    queryOptions.sort = sort;
    if (options.offset) queryOptions.skip = options.offset;
    if (options.limit) queryOptions.limit = options.limit;
    queryOptions.fields = getFieldsProjection(options.fields);

    collection.find({}, queryOptions).toArray(cb);
  };

  store.create = function (attrs, cb) {
    if (typeof attrs === 'function') {
      cb = attrs;
      attrs = {};
    }
    attrs || (attrs = {});
    var entity = copy(attrs);
    if (typeof entity.id === 'undefined') entity.id = idgen(16);
    if (typeof entity.created === 'undefined') entity.created = new Date();
    if (typeof entity.rev === 'undefined') entity.rev = 0;
    if (opts.create) opts.create(entity);
    if (cb) process.nextTick(function () {
      store.save(entity, cb);
    });
    return entity;
  };

  store.save = function (entity, options, cb) {
    if (!cb) {
      if ('function' === typeof options) {
        cb = options;
        options = {};
      }
      else {
        cb = defaultCb;
      }
    }
    options = _.defaults(options || {}, {
      w: 1, // by default, at least wait for acknowledgement of write
      new: true // return the updated document instead of the old one
    });
    options.fields = getFieldsProjection(options.fields);
    var rev = entity.rev || 0;
    entity.rev++;
    entity.updated = new Date();
    var changed = entity._changed = rev > 0 ? createChangedHash(entity) : {};

    if (opts.save) opts.save(entity, doSave);
    else doSave();

    function doSave (err, saveEntity) {
      if (!saveEntity) saveEntity = entity;
      else if (rev > 0) changed = createChangedHash(entity);
      if (err) return cb(err);

      options.upsert = rev > 0 ? false : true;
      var setEntity;
      if (rev > 0) {
        setEntity = copy(saveEntity);
        unchangeables.forEach(function (prop) {
          nested.delete(setEntity, prop);
        });
      }
      setEntity || (setEntity = saveEntity);
      delete setEntity._changed;
      collection.findAndModify({ id: saveEntity.id, rev: rev }, null, { $set: setEntity }, options, function (err, resultEntity) {
        if (err) return cb(err);
        if (!resultEntity) return cb(null, null);
        resultEntity._changed = changed;
        if (opts.afterSave) opts.afterSave(resultEntity, function (err) {
          if (err) return cb(err);
          cb(null, resultEntity);
        });
        else cb(null, resultEntity);
      });
    }
  };

  store.load = function (id, options, cb) {
    if (!cb) {
      if ('function' === typeof options) {
        cb = options;
        options = {};
      }
      else {
        cb = defaultCb;
      }
    }
    options || (options = {});
    options.fields = getFieldsProjection(options.fields);
    var entity;
    if ({}.toString.call(id) === '[object Object]') entity = id;
    collection.findOne(entity || { id: id }, options, function (err, entity) {
      if (err) return cb(err);
      if (opts.load) opts.load(entity, doCallback);
      else doCallback();

      function doCallback (err, loadEntity) {
        if (err) return cb(err);
        if (!loadEntity) loadEntity = entity;
        cb(null, loadEntity);
      }

    });
  };

  store.destroy = function (id, options, cb) {
    if (!cb) {
      if ('function' === typeof options) {
        cb = options;
        options = {};
      }
      else {
        cb = defaultCb;
      }
    }
    var entity, query;
    if (id.id) entity = id, id = id.id;
    if (entity) {
      query = { id: entity.id, rev: entity.rev };
    }
    else {
      query = { id: id };
    }
    options = _.defaults(options || {}, {
      w: 1 // by default, at least wait for acknowledgement of write
    });

    if (opts.destroy) {
      store.load(query, function (err, loadEntity) {
        if (err) return cb(err);
        if (!loadEntity) return cb();
        query.rev || (query.rev = loadEntity.rev);
        opts.destroy(loadEntity, doDestroy);
      });
    }
    else doDestroy();

    function doDestroy (err) {
      if (err) return cb(err);
      collection.findAndRemove(query, null, null, options, function (err, destroyedEntity) {
        if (err) return cb(err);
        if (opts.afterDestroy) opts.afterDestroy(destroyedEntity, function (err) {
          if (err) return cb(err);
          cb(null);
        });
        else cb(null);
      });
    }
  };

  store.options = opts;
  return store;
};

var unchangeables = [ '_id', 'id', 'created' ];

function defaultCb (err) {
  if (err) app.emit('error', err);
}

function copy (obj) {
  var c = {};
  Object.keys(obj).forEach(function (prop) {
    c[prop] = obj[prop];
  });
  return c;
}

var flatArrayIdxRe = /\.\d+$/
  , changedRe = /^_changed/;

/**
 * Takes an object and returns a shallow object keyed with path names each having
 * the value Boolean(true)
 *
 * Ex:
 * ```js
 * var obj = {
 *   a : {
 *     b: {
 *       c: [1,2,3]
 *     },
 *     d: "abc"
 *   },
 *   e: new Date()
 * };
 * createChangedHash(obj);
 * // returns { 'a.b.c': true,
 * //           'a.d': true,
 * //           e: true }
 */
function createChangedHash (obj) {
  var flat = nested.flatten(obj)
    , changed = {};
  Object.keys(flat).forEach(function (key) {
    if (changedRe.test(key) || ~unchangeables.indexOf(key)) return;
    var trimmed = key.replace(flatArrayIdxRe, '');
    if (trimmed !== key) {
      if (!(trimmed in changed)) {
        changed[trimmed] = true;
      }
      delete changed[key];
    }
    else {
      changed[key] = true;
    }
  });
  return changed;
}
<commit_msg>Passes query object to collection.find<commit_after>var app = require('cantina')
  , idgen = require('idgen')
  , nested = require('nested-objects')
  , _ = require('underscore');

// Note that unlike modeler, we do not guarantee that you can list documents
// in insertion order

module.exports = function (_opts) {
  var opts = copy(_opts);
  if (!opts.db) throw new Error('must pass a node-mongodb-native db with options.db');
  if (!opts.name) throw new Error('must pass a collection name with options.name');

  // We promise these will always* be in the model
  // *updated is only there if the model have been saved
  var requiredProperties = [ 'id', 'rev', 'created', 'updated' ];

  var store = {}
    , db = opts.db
    , collection = db.collection(opts.name)
    , privateProperties = (opts.privateProperties && opts.privateProperties.filter(function (prop) {
      return requiredProperties.indexOf(prop) === -1;
    })) || [];

  Object.keys(collection.constructor.prototype).forEach(function (prop) {
    if (typeof this[prop] === 'function') store['_' + prop] = this[prop].bind(collection);
    else store['_' + prop] = collection[prop];
  }, collection.constructor.prototype);

  // Takes a field projection: http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/#read-operations-projection
  // and returns a projection combined with the store's default private properties
  // Performs minimal validation on the passed fields, so if you pass in an invalid
  // projection (e.g., { foo: 1, bar: 0 }) you will get unpredictable results.
  function getFieldsProjection (fields) {
    var projection = (fields && copy(fields)) || {}
      , include;
    Object.keys(projection).forEach(function (field) {
      var flag = +projection[field];
      if (typeof include === 'undefined') include = flag;
      // can't mix and match flags, except for special case exluding _id
      else if (include !== flag && field !== '_id' && include !== 1 && flag !== 0) delete projection[field];
    });
    if (include !== 1) {
      privateProperties.forEach(function (field) {
        projection[field] = 0;
      });
      if (include === 0) {
        requiredProperties.forEach(function (field) {
          if (field in projection) delete projection[field];
        });
      }
    }
    else {
      // No. We really don't allow private properties to be in the results.
      privateProperties.forEach(function (field) {
        if (field in projection) delete projection[field];
      });
      requiredProperties.forEach(function (field) {
        projection[field] = 1;
      });
    }
    return Object.keys(projection).length ? projection : null;
  }

  // Ensure indexes exist
  // No other operations can happen while indexes are being created, so we
  // run this on initialize and only really care if there's an error
  collection.ensureIndex([{ "id": 1, "rev": 1 }], { unique: true }, function (err) {
    if (err) app.emit('error', err);
  });

  /**
   * @param {Object} query (optional)
   * @param {Object} options (optional, required if query is provided)
   * @param {Function} cb
   */
  store.list = function (query, options, cb) {
    if (arguments.length < 3) {
      if (typeof options === 'function') {
        cb = options;
        options = query;
        query = {};
      }
      else if (typeof query === 'function') {
        cb = query;
        options = {};
        query = {};
      }
    }
    options = _.defaults(options || {}, {
      sort: '_id',
      offset: 0,
      reverse: true
    });

    // translate modeler keys to mongo keys
    var queryOptions = {}, sort = {};
    sort[options.sort] = options.reverse ? -1 : 1;
    queryOptions.sort = sort;
    if (options.offset) queryOptions.skip = options.offset;
    if (options.limit) queryOptions.limit = options.limit;
    queryOptions.fields = getFieldsProjection(options.fields);

    collection.find(query, queryOptions).toArray(cb);
  };

  store.create = function (attrs, cb) {
    if (typeof attrs === 'function') {
      cb = attrs;
      attrs = {};
    }
    attrs || (attrs = {});
    var entity = copy(attrs);
    if (typeof entity.id === 'undefined') entity.id = idgen(16);
    if (typeof entity.created === 'undefined') entity.created = new Date();
    if (typeof entity.rev === 'undefined') entity.rev = 0;
    if (opts.create) opts.create(entity);
    if (cb) process.nextTick(function () {
      store.save(entity, cb);
    });
    return entity;
  };

  store.save = function (entity, options, cb) {
    if (!cb) {
      if ('function' === typeof options) {
        cb = options;
        options = {};
      }
      else {
        cb = defaultCb;
      }
    }
    options = _.defaults(options || {}, {
      w: 1, // by default, at least wait for acknowledgement of write
      new: true // return the updated document instead of the old one
    });
    options.fields = getFieldsProjection(options.fields);
    var rev = entity.rev || 0;
    entity.rev++;
    entity.updated = new Date();
    var changed = entity._changed = rev > 0 ? createChangedHash(entity) : {};

    if (opts.save) opts.save(entity, doSave);
    else doSave();

    function doSave (err, saveEntity) {
      if (!saveEntity) saveEntity = entity;
      else if (rev > 0) changed = createChangedHash(entity);
      if (err) return cb(err);

      options.upsert = rev > 0 ? false : true;
      var setEntity;
      if (rev > 0) {
        setEntity = copy(saveEntity);
        unchangeables.forEach(function (prop) {
          nested.delete(setEntity, prop);
        });
      }
      setEntity || (setEntity = saveEntity);
      delete setEntity._changed;
      collection.findAndModify({ id: saveEntity.id, rev: rev }, null, { $set: setEntity }, options, function (err, resultEntity) {
        if (err) return cb(err);
        if (!resultEntity) return cb(null, null);
        resultEntity._changed = changed;
        if (opts.afterSave) opts.afterSave(resultEntity, function (err) {
          if (err) return cb(err);
          cb(null, resultEntity);
        });
        else cb(null, resultEntity);
      });
    }
  };

  store.load = function (id, options, cb) {
    if (!cb) {
      if ('function' === typeof options) {
        cb = options;
        options = {};
      }
      else {
        cb = defaultCb;
      }
    }
    options || (options = {});
    options.fields = getFieldsProjection(options.fields);
    var entity;
    if ({}.toString.call(id) === '[object Object]') entity = id;
    collection.findOne(entity || { id: id }, options, function (err, entity) {
      if (err) return cb(err);
      if (opts.load) opts.load(entity, doCallback);
      else doCallback();

      function doCallback (err, loadEntity) {
        if (err) return cb(err);
        if (!loadEntity) loadEntity = entity;
        cb(null, loadEntity);
      }

    });
  };

  store.destroy = function (id, options, cb) {
    if (!cb) {
      if ('function' === typeof options) {
        cb = options;
        options = {};
      }
      else {
        cb = defaultCb;
      }
    }
    var entity, query;
    if (id.id) entity = id, id = id.id;
    if (entity) {
      query = { id: entity.id, rev: entity.rev };
    }
    else {
      query = { id: id };
    }
    options = _.defaults(options || {}, {
      w: 1 // by default, at least wait for acknowledgement of write
    });

    if (opts.destroy) {
      store.load(query, function (err, loadEntity) {
        if (err) return cb(err);
        if (!loadEntity) return cb();
        query.rev || (query.rev = loadEntity.rev);
        opts.destroy(loadEntity, doDestroy);
      });
    }
    else doDestroy();

    function doDestroy (err) {
      if (err) return cb(err);
      collection.findAndRemove(query, null, null, options, function (err, destroyedEntity) {
        if (err) return cb(err);
        if (opts.afterDestroy) opts.afterDestroy(destroyedEntity, function (err) {
          if (err) return cb(err);
          cb(null);
        });
        else cb(null);
      });
    }
  };

  store.options = opts;
  return store;
};

var unchangeables = [ '_id', 'id', 'created' ];

function defaultCb (err) {
  if (err) app.emit('error', err);
}

function copy (obj) {
  var c = {};
  Object.keys(obj).forEach(function (prop) {
    c[prop] = obj[prop];
  });
  return c;
}

var flatArrayIdxRe = /\.\d+$/
  , changedRe = /^_changed/;

/**
 * Takes an object and returns a shallow object keyed with path names each having
 * the value Boolean(true)
 *
 * Ex:
 * ```js
 * var obj = {
 *   a : {
 *     b: {
 *       c: [1,2,3]
 *     },
 *     d: "abc"
 *   },
 *   e: new Date()
 * };
 * createChangedHash(obj);
 * // returns { 'a.b.c': true,
 * //           'a.d': true,
 * //           e: true }
 */
function createChangedHash (obj) {
  var flat = nested.flatten(obj)
    , changed = {};
  Object.keys(flat).forEach(function (key) {
    if (changedRe.test(key) || ~unchangeables.indexOf(key)) return;
    var trimmed = key.replace(flatArrayIdxRe, '');
    if (trimmed !== key) {
      if (!(trimmed in changed)) {
        changed[trimmed] = true;
      }
      delete changed[key];
    }
    else {
      changed[key] = true;
    }
  });
  return changed;
}
<|endoftext|>


<commit_before>import _               from 'lodash';
import wpi, { OUTPUT } from 'wiring-pi';
import EventEmitter    from 'events';
import NanoTimer       from 'nanotimer';

/**
 * Node's EventEmitter module
 * @external EventEmitter
 * @see {@link https://nodejs.org/api/events.html}
 */

/**
 * A Bunyan `Logger` instance
 * @external Logger
 * @see {@link https://www.npmjs.com/package/bunyan}
 */

/**
 * Represents a single step of the motor, indicating which pins should be active.
 * Must have the same number of members as `this.pins.length`.
 * @typedef {number[]} Phase
 */

/**
 * An array of Phases, each representing a single motor step
 * @typedef {Phase[]} Mode
 */

/**
 * @constant
 * @type {Object.<string,Mode>}
 * @default
 */
export const MODES = {
  SINGLE: [
    [ 1, 0, 0, 0 ],
    [ 0, 1, 0, 0 ],
    [ 0, 0, 1, 0 ],
    [ 0, 0, 0, 1 ]
  ],
  DUAL: [
    [ 1, 0, 0, 1 ],
    [ 0, 1, 0, 1 ],
    [ 0, 1, 1, 0 ],
    [ 1, 0, 1, 0 ]
  ]
};

/**
 * Positive number denotes forward motion, negative denotes backward motion.
 * @typedef {number} Direction
 */

/**
 * @constant
 * @type {Direction}
 * @default
 */
export const FORWARD = 1;

/**
 * @constant
 * @type {Direction}
 * @default
 */
export const BACKWARD = -1;

/**
 * Stepper motor control class
 * @extends EventEmitter
 */
export class Stepper extends EventEmitter {
  /**
   * Create a stepper motor controller instance
   * @param {Object} config - An object of configuration parameters
   * @param {number[]} config.pins - An array of Raspberry Pi GPIO pin numbers _(NOT WiringPi pin numbers)_
   * @param {number} [config.steps=200] - The number of steps per motor revolution
   * @param {Mode} [config.mode=MODES.DUAL] - GPIO pin activation sequence
   * @param {number} [config.speed=1] - Motor rotation speed in RPM
   * @example
Initialize a stepper controller on pins 17, 16, 13, and 12, for a motor with 200 steps per revolution

   * import { Stepper } from 'wpi-stepper';
   * const motor = new Stepper({ pins: [ 17, 16, 13, 12 ], steps: 200 });
   * @returns {Object} an instance of Stepper
   */
  constructor({ pins, steps = 200, mode = MODES.DUAL, speed = 1 }) {
    super();
    this.mode       = mode;
    this.pins       = pins;
    this.steps      = steps;
    this.stepNum    = 0;
    this.moving     = false;
    this.direction  = null;
    this.speed      = speed;
    this._moveTimer = new NanoTimer();
    this._powered   = false;

    this._validateOptions();
    wpi.setup('gpio');

    for (let pin of this.pins) {
      wpi.pinMode(pin, OUTPUT);
    }
  }

  /**
   * The maximum speed at which the motor can rotate (as dictated by our
   * timing resolution). _Note: This is not your motor's top speed; it's the computer's.
   * This library has not been tested with actual motor speeds in excess of 300 RPM.
   * @type {number}
   */
  get maxRPM() {
    return 60 * 1e6 / this.steps;
  }

  /**
   * Returns the absolute value of the current motor step
   * @type {number}
   */
  get absoluteStep() {
    return Math.abs(this.stepNum);
  }

  /**
   * Set motor speed in RPM
   * @type {number}
   * @param {number} rpm - The number of RPMs
   * @fires Stepper#speed
   * @example
Sets the speed to 20 RPM

   * motor.speed = 20;
   * // => 20
   * @example
Receive notifications when motor speed changes

   * motor.on('speed', (rpms, stepDelay) => console.log('RPM: %d, Step Delay: %d', rpms, stepDelay));
   * motor.speed = 20;
   * // => 20
   * // => "RPM: 20, Step Delay: 15000"
   */
  set speed(rpm) {
    this._rpms = rpm;

    if (this._rpms > this.maxRPM) {
      this._rpms = this.maxRPM;
    }

    this._stepDelay = this.maxRPM / this._rpms;

    /**
     * Speed change event
     * @event Stepper#speed
     * @param {number} rpms - The current RPM number
     * @param {number} stepDelay - The current step delay in microseconds
     */
    this.emit('speed', this._rpms, this._stepDelay);
  }

  get speed() {
    return this._rpms;
  }

  /**
   * Stop the motor and power down all GPIO pins
   * @fires Stepper#stop
   * @example
Log to console whenever the motor stops

   * motor.on('stop', () => console.log('Motor stopped'));
   * motor.stop();
   * // => undefined
   * // => "Motor stopped"
   * @returns {undefined}
   */
  stop() {
    this._stopMoving();
    this._powerDown();

    /**
     * Fires when the motor stops moving AND powers off all magnets
     * @event Stepper#stop
     */
    this.emit('stop');
  }

  /**
   * Stop moving the motor and hold position
   * @fires Stepper#hold
   * @example
Log to console when the motor holds position

   * motor.on('hold', () => console.log('Holding position'));
   * motor.hold();
   * // => undefined
   * // => "Holding position"
   * @returns {undefined}
   */
  hold() {
    this._stopMoving();

    /**
     * Fires when the motor stops moving and holds its current position
     * @event Stepper#hold
     */
    this.emit('hold');
  }

  /**
   * Move the motor a specified number of steps. Each step fires a `move` event. If another call to `move()`
   * is made while a motion is still executing, the previous motion will be cancelled and a `cancel` event
   * will fire.
   * @param {number} stepsToMove - Positive for forward, negative for backward
   * @fires Stepper#start
   * @fires Stepper#move
   * @fires Stepper#complete
   * @fires Stepper#hold
   * @fires Stepper#cancel
   * @example
Move the motor forward one full rotation, then log to console

   * motor.move(200).then(() => console.log('Motion complete'));
   * // => Promise
   * // => "Motion complete"
   * @example
Same thing, using an event handler instead of a promise

   * motor.on('complete', () => console.log('Motion complete'));
   * motor.move(200);
   * // => Promise
   * // => "Motion complete"
   * @returns {Promise.<number>} A promise resolving to the number of steps moved
   */
  move(stepsToMove) {
    if (stepsToMove === 0) {
      return this.hold();
    }

    if (this.moving) {
      /**
       * Emitted when a motion is cancelled, before a new one begins
       * @event Stepper#cancel
       */
      this.emit('cancel');
      this.hold();
    }

    this.moving    = true;
    let remaining  = Math.abs(stepsToMove);
    this.direction = stepsToMove > 0 ? FORWARD : BACKWARD;

    /**
     * Fires right before a new motion begins
     * @event Stepper#start
     * @param {Direction} direction
     * @param {number} stepsToMove - The requested number of steps to move
     */
    this.emit('start', this.direction, stepsToMove);

    return new Promise((resolve) => {
      this._moveTimer.setInterval(() => {
        if (remaining === 0) {
          /**
           * Fires when a motion is completed and there are no more steps to move, right before the motor holds position
           * @event Stepper#complete
           */
          this.emit('complete');
          this.hold();
          return resolve(this.stepNum);
        }

        this.step(this.direction);
        remaining--;
      }, '', `${this._stepDelay}u`);
    });
  }

  /**
   * Run the motor in the given direction indefinitely
   * @fires Stepper#cancel
   * @fires Stepper#start
   * @fires Stepper#move
   * @fires Stepper#complete
   * @fires Stepper#hold
   * @param {Direction} [direction=FORWARD] - The direction in which to move (`FORWARD` or `BACKWARD`)
   * @returns {undefined} nothing
   */
  run(direction = FORWARD) {
    this.move(direction * Infinity);
  }

  /**
   * Moves the motor a single step forward. Convenience method for `this.step(FORWARD)`.
   * @fires Stepper#move
   * @returns {number} The motor's current step number
   */
  stepForward() {
    return this.step(FORWARD);
  }

  /**
   * Moves the motor a single step backward. Convenience method for `this.step(BACKWARD)`.
   * @fires Stepper#move
   * @returns {number} The motor's current step number
   */
  stepBackward() {
    return this.step(BACKWARD);
  }

  /**
   * Move the motor one step in the given direction
   * @fires Stepper#move
   * @param {Direction} direction - The direction in which to move
   * @returns {number} The motor's current step number
   */
  step(direction) {
    let phase;

    if (!direction) {
      return;
    }

    this._countStep(direction);

    const pinStates = this.mode[phase];

    this._setPinStates(...pinStates);

    /**
     * Fires each time the motor moves a step
     * @event Stepper#move
     * @param {number} direction - 1 for forward, -1 for backward
     * @param {number} phase - Current pin activation phase
     * @param {number[]} pinStates - Current pin activation states
     *
     * @example
Log each step moved, in excruciating detail

     * motor.on('move', (direction, phase, pinStates) => {
     *   console.debug(
     *     'Moved one step (direction: %d, phase: %O, pinStates: %O)',
     *     direction,
     *     phase,
     *     pinStates
     *   );
     * });
     * motor.move(200);
     * // => Promise
     */
    this.emit('move', direction, phase, pinStates);

    return this.stepNum;
  }

  /**
   * Attach a `bunyan` logger instance to report on all possible events at varying detail levels.
   * @param {Logger} logger - a Bunyan logger instance
   * @returns {undefined}
   */
  attachLogger(logger) {
    const childLog = logger.child({ module: 'Stepper' });

    this.on('power', () => childLog.info({ powered: this._powered }, 'power toggled'));
    this.on('speed', () => childLog.info({ rpms: this._rpms, stepDelay: this._stepDelay }, 'speed changed'));
    this.on('hold', () => childLog.info('holding position'));
    this.on('start', (direction, steps) => childLog.info({ direction, steps }, 'starting motion'));
    this.on('stop', () => childLog.info('stopping'));
    this.on('cancel', () => childLog.info('cancelling previous motion'));
    this.on('move', (direction, phase, pinStates) => {
      childLog.debug({ direction, phase, pinStates }, 'move one step');
    });
    this.on('complete', () => childLog.info({ numSteps: this._numSteps }, 'motion complete'));
  }

  /**
   * Stop all queued motion
   * @private
   * @returns {undefined}
   */
  _stopMoving() {
    this._resetMoveTimer();
    this.moving = false;
  }

  _powerDown() {
    let pins      = [ ...this.pins ];
    this._powered = false;

    _.fill(pins, 0);
    this._setPinStates(...pins);
    this.emit('power', false);
  }

  _setPinStates(...states) {
    if (states.length !== this.pins.length) {
      throw new Error(`Must pass exactly ${this.pins.length} pin states`);
    }

    for (let idx in states) {
      wpi.digitalWrite(this.pins[idx], states[idx]);

      if (!this._powered && states[idx] === 1) {
        this._powered = true;
        this.emit('power', true);
      }
    }
  }

  _resetMoveTimer() {
    this._moveTimer.clearInterval();
  }

  _countStep(direction) {
    this.stepNum += direction;

    if (this.stepNum >= this.steps) {
      this.stepNum = 0;
    } else if (this.stepNum < 0) {
      this.stepNum = this.steps - 1;
    }

    return this.absoluteStep % this.mode.length;
  }

  _validateOptions() {
    const { mode, pins } = this;
    const invalidStep    = _.findIndex(mode, (step) => step.length !== pins.length);

    if (invalidStep !== -1) {
      throw new Error(`Mode step at index ${invalidStep} has the wrong number of pins`);
    }
  }
}

export default Stepper;
<commit_msg>Use countStep's return value to pick activation phase<commit_after>import _               from 'lodash';
import wpi, { OUTPUT } from 'wiring-pi';
import EventEmitter    from 'events';
import NanoTimer       from 'nanotimer';

/**
 * Node's EventEmitter module
 * @external EventEmitter
 * @see {@link https://nodejs.org/api/events.html}
 */

/**
 * A Bunyan `Logger` instance
 * @external Logger
 * @see {@link https://www.npmjs.com/package/bunyan}
 */

/**
 * Represents a single step of the motor, indicating which pins should be active.
 * Must have the same number of members as `this.pins.length`.
 * @typedef {number[]} Phase
 */

/**
 * An array of Phases, each representing a single motor step
 * @typedef {Phase[]} Mode
 */

/**
 * @constant
 * @type {Object.<string,Mode>}
 * @default
 */
export const MODES = {
  SINGLE: [
    [ 1, 0, 0, 0 ],
    [ 0, 1, 0, 0 ],
    [ 0, 0, 1, 0 ],
    [ 0, 0, 0, 1 ]
  ],
  DUAL: [
    [ 1, 0, 0, 1 ],
    [ 0, 1, 0, 1 ],
    [ 0, 1, 1, 0 ],
    [ 1, 0, 1, 0 ]
  ]
};

/**
 * Positive number denotes forward motion, negative denotes backward motion.
 * @typedef {number} Direction
 */

/**
 * @constant
 * @type {Direction}
 * @default
 */
export const FORWARD = 1;

/**
 * @constant
 * @type {Direction}
 * @default
 */
export const BACKWARD = -1;

/**
 * Stepper motor control class
 * @extends EventEmitter
 */
export class Stepper extends EventEmitter {
  /**
   * Create a stepper motor controller instance
   * @param {Object} config - An object of configuration parameters
   * @param {number[]} config.pins - An array of Raspberry Pi GPIO pin numbers _(NOT WiringPi pin numbers)_
   * @param {number} [config.steps=200] - The number of steps per motor revolution
   * @param {Mode} [config.mode=MODES.DUAL] - GPIO pin activation sequence
   * @param {number} [config.speed=1] - Motor rotation speed in RPM
   * @example
Initialize a stepper controller on pins 17, 16, 13, and 12, for a motor with 200 steps per revolution

   * import { Stepper } from 'wpi-stepper';
   * const motor = new Stepper({ pins: [ 17, 16, 13, 12 ], steps: 200 });
   * @returns {Object} an instance of Stepper
   */
  constructor({ pins, steps = 200, mode = MODES.DUAL, speed = 1 }) {
    super();
    this.mode       = mode;
    this.pins       = pins;
    this.steps      = steps;
    this.stepNum    = 0;
    this.moving     = false;
    this.direction  = null;
    this.speed      = speed;
    this._moveTimer = new NanoTimer();
    this._powered   = false;

    this._validateOptions();
    wpi.setup('gpio');

    for (let pin of this.pins) {
      wpi.pinMode(pin, OUTPUT);
    }
  }

  /**
   * The maximum speed at which the motor can rotate (as dictated by our
   * timing resolution). _Note: This is not your motor's top speed; it's the computer's.
   * This library has not been tested with actual motor speeds in excess of 300 RPM.
   * @type {number}
   */
  get maxRPM() {
    return 60 * 1e6 / this.steps;
  }

  /**
   * Returns the absolute value of the current motor step
   * @type {number}
   */
  get absoluteStep() {
    return Math.abs(this.stepNum);
  }

  /**
   * Set motor speed in RPM
   * @type {number}
   * @param {number} rpm - The number of RPMs
   * @fires Stepper#speed
   * @example
Sets the speed to 20 RPM

   * motor.speed = 20;
   * // => 20
   * @example
Receive notifications when motor speed changes

   * motor.on('speed', (rpms, stepDelay) => console.log('RPM: %d, Step Delay: %d', rpms, stepDelay));
   * motor.speed = 20;
   * // => 20
   * // => "RPM: 20, Step Delay: 15000"
   */
  set speed(rpm) {
    this._rpms = rpm;

    if (this._rpms > this.maxRPM) {
      this._rpms = this.maxRPM;
    }

    this._stepDelay = this.maxRPM / this._rpms;

    /**
     * Speed change event
     * @event Stepper#speed
     * @param {number} rpms - The current RPM number
     * @param {number} stepDelay - The current step delay in microseconds
     */
    this.emit('speed', this._rpms, this._stepDelay);
  }

  get speed() {
    return this._rpms;
  }

  /**
   * Stop the motor and power down all GPIO pins
   * @fires Stepper#stop
   * @example
Log to console whenever the motor stops

   * motor.on('stop', () => console.log('Motor stopped'));
   * motor.stop();
   * // => undefined
   * // => "Motor stopped"
   * @returns {undefined}
   */
  stop() {
    this._stopMoving();
    this._powerDown();

    /**
     * Fires when the motor stops moving AND powers off all magnets
     * @event Stepper#stop
     */
    this.emit('stop');
  }

  /**
   * Stop moving the motor and hold position
   * @fires Stepper#hold
   * @example
Log to console when the motor holds position

   * motor.on('hold', () => console.log('Holding position'));
   * motor.hold();
   * // => undefined
   * // => "Holding position"
   * @returns {undefined}
   */
  hold() {
    this._stopMoving();

    /**
     * Fires when the motor stops moving and holds its current position
     * @event Stepper#hold
     */
    this.emit('hold');
  }

  /**
   * Move the motor a specified number of steps. Each step fires a `move` event. If another call to `move()`
   * is made while a motion is still executing, the previous motion will be cancelled and a `cancel` event
   * will fire.
   * @param {number} stepsToMove - Positive for forward, negative for backward
   * @fires Stepper#start
   * @fires Stepper#move
   * @fires Stepper#complete
   * @fires Stepper#hold
   * @fires Stepper#cancel
   * @example
Move the motor forward one full rotation, then log to console

   * motor.move(200).then(() => console.log('Motion complete'));
   * // => Promise
   * // => "Motion complete"
   * @example
Same thing, using an event handler instead of a promise

   * motor.on('complete', () => console.log('Motion complete'));
   * motor.move(200);
   * // => Promise
   * // => "Motion complete"
   * @returns {Promise.<number>} A promise resolving to the number of steps moved
   */
  move(stepsToMove) {
    if (stepsToMove === 0) {
      return this.hold();
    }

    if (this.moving) {
      /**
       * Emitted when a motion is cancelled, before a new one begins
       * @event Stepper#cancel
       */
      this.emit('cancel');
      this.hold();
    }

    this.moving    = true;
    let remaining  = Math.abs(stepsToMove);
    this.direction = stepsToMove > 0 ? FORWARD : BACKWARD;

    /**
     * Fires right before a new motion begins
     * @event Stepper#start
     * @param {Direction} direction
     * @param {number} stepsToMove - The requested number of steps to move
     */
    this.emit('start', this.direction, stepsToMove);

    return new Promise((resolve) => {
      this._moveTimer.setInterval(() => {
        if (remaining === 0) {
          /**
           * Fires when a motion is completed and there are no more steps to move, right before the motor holds position
           * @event Stepper#complete
           */
          this.emit('complete');
          this.hold();
          return resolve(this.stepNum);
        }

        this.step(this.direction);
        remaining--;
      }, '', `${this._stepDelay}u`);
    });
  }

  /**
   * Run the motor in the given direction indefinitely
   * @fires Stepper#cancel
   * @fires Stepper#start
   * @fires Stepper#move
   * @fires Stepper#complete
   * @fires Stepper#hold
   * @param {Direction} [direction=FORWARD] - The direction in which to move (`FORWARD` or `BACKWARD`)
   * @returns {undefined} nothing
   */
  run(direction = FORWARD) {
    this.move(direction * Infinity);
  }

  /**
   * Moves the motor a single step forward. Convenience method for `this.step(FORWARD)`.
   * @fires Stepper#move
   * @returns {number} The motor's current step number
   */
  stepForward() {
    return this.step(FORWARD);
  }

  /**
   * Moves the motor a single step backward. Convenience method for `this.step(BACKWARD)`.
   * @fires Stepper#move
   * @returns {number} The motor's current step number
   */
  stepBackward() {
    return this.step(BACKWARD);
  }

  /**
   * Move the motor one step in the given direction
   * @fires Stepper#move
   * @param {Direction} direction - The direction in which to move
   * @returns {number} The motor's current step number
   */
  step(direction) {
    if (!direction) {
      return;
    }

    const phase     = this._countStep(direction);
    const pinStates = this.mode[phase];

    this._setPinStates(...pinStates);

    /**
     * Fires each time the motor moves a step
     * @event Stepper#move
     * @param {number} direction - 1 for forward, -1 for backward
     * @param {number} phase - Current pin activation phase
     * @param {number[]} pinStates - Current pin activation states
     *
     * @example
Log each step moved, in excruciating detail

     * motor.on('move', (direction, phase, pinStates) => {
     *   console.debug(
     *     'Moved one step (direction: %d, phase: %O, pinStates: %O)',
     *     direction,
     *     phase,
     *     pinStates
     *   );
     * });
     * motor.move(200);
     * // => Promise
     */
    this.emit('move', direction, phase, pinStates);

    return this.stepNum;
  }

  /**
   * Attach a `bunyan` logger instance to report on all possible events at varying detail levels.
   * @param {Logger} logger - a Bunyan logger instance
   * @returns {undefined}
   */
  attachLogger(logger) {
    const childLog = logger.child({ module: 'Stepper' });

    this.on('power', () => childLog.info({ powered: this._powered }, 'power toggled'));
    this.on('speed', () => childLog.info({ rpms: this._rpms, stepDelay: this._stepDelay }, 'speed changed'));
    this.on('hold', () => childLog.info('holding position'));
    this.on('start', (direction, steps) => childLog.info({ direction, steps }, 'starting motion'));
    this.on('stop', () => childLog.info('stopping'));
    this.on('cancel', () => childLog.info('cancelling previous motion'));
    this.on('move', (direction, phase, pinStates) => {
      childLog.debug({ direction, phase, pinStates }, 'move one step');
    });
    this.on('complete', () => childLog.info({ numSteps: this._numSteps }, 'motion complete'));
  }

  /**
   * Stop all queued motion
   * @private
   * @returns {undefined}
   */
  _stopMoving() {
    this._resetMoveTimer();
    this.moving = false;
  }

  _powerDown() {
    let pins      = [ ...this.pins ];
    this._powered = false;

    _.fill(pins, 0);
    this._setPinStates(...pins);
    this.emit('power', false);
  }

  _setPinStates(...states) {
    if (states.length !== this.pins.length) {
      throw new Error(`Must pass exactly ${this.pins.length} pin states`);
    }

    for (let idx in states) {
      wpi.digitalWrite(this.pins[idx], states[idx]);

      if (!this._powered && states[idx] === 1) {
        this._powered = true;
        this.emit('power', true);
      }
    }
  }

  _resetMoveTimer() {
    this._moveTimer.clearInterval();
  }

  _countStep(direction) {
    this.stepNum += direction;

    if (this.stepNum >= this.steps) {
      this.stepNum = 0;
    } else if (this.stepNum < 0) {
      this.stepNum = this.steps - 1;
    }

    return this.absoluteStep % this.mode.length;
  }

  _validateOptions() {
    const { mode, pins } = this;
    const invalidStep    = _.findIndex(mode, (step) => step.length !== pins.length);

    if (invalidStep !== -1) {
      throw new Error(`Mode step at index ${invalidStep} has the wrong number of pins`);
    }
  }
}

export default Stepper;
<|endoftext|>


<commit_before>function Id(a) {
    this.value = a;
}

// Semigroup (value must also be a Semigroup)
Id.prototype.concat = function(b) {
    return new Id(this.value.concat(b.value));
};

// Monoid (value must also be a Monoid)
Id.prototype.empty = function() {
    return new Id(this.value.empty ? this.value.empty() : this.value.constructor.empty());
};

// Functor
Id.prototype.map = function(f) {
    return new Id(f(this.value));
};

// Applicative
Id.prototype.ap = function(b) {
    return new Id(this.value(b.value));
};

// Chain
Id.prototype.chain = function(f) {
    return f(this.value);
};

// Monad
Id.of = function(a) {
    return new Id(a);
};

if(typeof module == 'object') module.exports = Id;
<commit_msg>Add extend and from to Id spec<commit_after>function Id(a) {
    this.value = a;
}

// Semigroup (value must also be a Semigroup)
Id.prototype.concat = function(b) {
    return new Id(this.value.concat(b.value));
};

// Monoid (value must also be a Monoid)
Id.prototype.empty = function() {
    return new Id(this.value.empty ? this.value.empty() : this.value.constructor.empty());
};

// Functor
Id.prototype.map = function(f) {
    return new Id(f(this.value));
};

// Applicative
Id.prototype.ap = function(b) {
    return new Id(this.value(b.value));
};

// Chain
Id.prototype.chain = function(f) {
    return f(this.value);
};

// Extend
Id.prototype.extend = function(f){
  return new Id(f(this.value));
}

// Monad
Id.of = function(a) {
    return new Id(a);
};

// Comonad
Id.from = function(a){
  return a.value;
}

if(typeof module == 'object') module.exports = Id;
<|endoftext|>


<commit_before>document.addEventListener('DOMContentLoaded', startGame)

// Define your `board` object here!
var board = {
  cells: [
    { row: 0, col: 0, isMarked: false, isMine: false, hidden: true },
    { row: 2, col: 0, isMarked: false, isMine: false, hidden: true },
    { row: 1, col: 2, isMarked: false, isMine: false, hidden: true },
    { row: 0, col: 1, isMarked: false, isMine: false, hidden: true },
    { row: 0, col: 2, isMarked: false, isMine: true, hidden: true },
    { row: 1, col: 0, isMarked: false, isMine: true, hidden: true },
    { row: 1, col: 1, isMarked: false, isMine: false, hidden: true },
    { row: 2, col: 1, isMarked: false, isMine: false, hidden: true },
    { row: 2, col: 2, isMarked: false, isMine: false, hidden: true }
  ]
}

function startGame () {
  for (var i = 0; i < board.cells.length; i++) {
    board.cells[i].surroundingMines = countSurroundingMines(board.cells[i])
  }

  document.addEventListener('click', checkForWin)
  document.addEventListener('contextmenu', checkForWin)

  // Don't remove this function call: it makes the game work!
  lib.initBoard()
}

// Define this function to look for a win condition:
//
// 1. Are all of the cells that are NOT mines visible?
// 2. Are all of the mines marked?
function checkForWin () {
  // Here, loop through
  var remaining = board.cells
    .filter(function (cell) {
      return cell.hidden && !cell.isMarked
    })
  if (remaining.length > 0) {
    return
  }
  var incorrect = board.cells
    .filter(function (cell) {
      return cell.isMarked && !cell.isMine
    })
  if (incorrect.length > 0) {
    return
  }

  // You can use this function call to declare a winner (once you've
  // detected that they've won, that is!)
  lib.displayMessage('You win!')
}

// Define this function to count the number of mines around the cell
// (there could be as many as 8). You don't have to get the surrounding
// cells yourself! Just use `lib.getSurroundingCells`:
//
//   var surrounding = lib.getSurroundingCells(cell.row, cell.col)
//
// It will return cell objects in an array. You should loop through
// them, counting the number of times `cell.isMine` is true.
function countSurroundingMines (cell) {
  var surrounding = lib.getSurroundingCells(cell.row, cell.col)
  return surrounding.filter(function (adjacentCell) {
    return adjacentCell.isMine
  }).length;
}

<commit_msg>Remove solution<commit_after>document.addEventListener('DOMContentLoaded', startGame)

// Define your `board` object here!
// var board =

function startGame () {
  // Don't remove this function call: it makes the game work!
  lib.initBoard()
}

// Define this function to look for a win condition:
//
// 1. Are all of the cells that are NOT mines visible?
// 2. Are all of the mines marked?
function checkForWin () {

  // You can use this function call to declare a winner (once you've
  // detected that they've won, that is!)
  //   lib.displayMessage('You win!')
}

// Define this function to count the number of mines around the cell
// (there could be as many as 8). You don't have to get the surrounding
// cells yourself! Just use `lib.getSurroundingCells`:
//
//   var surrounding = lib.getSurroundingCells(cell.row, cell.col)
//
// It will return cell objects in an array. You should loop through
// them, counting the number of times `cell.isMine` is true.
function countSurroundingMines (cell) {
}

<|endoftext|>


<commit_before>({
    baseUrl: "build/standalone/lib",
    mainConfigFile: "build/config/requirejs-config-dev.js",
    dir: "build/standalone-optimized/lib",
    findNestedDependencies: true,
    removeCombined: true,
    skipDirOptimize: true,
    useStrict: false,
    paths: {
        "browser_test": "empty:"
    },
    modules: [
        {
            name: "wed/wed",
            include: [
                "wed/modes/generic/generic",
                "wed/modes/generic/generic_meta"
            ],
            exclude: [
                "jquery",
                "bootstrap",
                "rangy",
                "jquery.bootstrap-growl",
                "log4javascript",
                "lodash"
            ]
        }
    ]
})
<commit_msg>Adjusted the exclusion list.<commit_after>({
    baseUrl: "build/standalone/lib",
    mainConfigFile: "build/config/requirejs-config-dev.js",
    dir: "build/standalone-optimized/lib",
    findNestedDependencies: true,
    removeCombined: true,
    skipDirOptimize: true,
    useStrict: false,
    paths: {
        "browser_test": "empty:"
    },
    modules: [
        {
            name: "wed/wed",
            include: [
                "wed/modes/generic/generic",
                "wed/modes/generic/generic_meta"
            ],
            exclude: [
                "jquery",
                "bootstrap",
                "log4javascript",
                "jquery.bootstrap-growl",
                "pubsub-js",
                "lodash"
            ]
        }
    ]
})
<|endoftext|>


<commit_before><commit_msg>Fixed bug in moveContentItemOnSlideSaga where moving the last item in a container would leave an unusable empty container on the slide; empty containers now get deleted.<commit_after><|endoftext|>


<commit_before>function Neighborlist3d(lc) {
  this.lc = lc;
  this.dr = 0.0;
  this.data = [];
  this.count = 0;
}

Neighborlist3d.prototype.clear = function() {
  for( var i = 0; i < this.lc.sim.atoms.length; ++i ) {
    this.data[i] = [];
  }
}
Neighborlist3d.prototype.update = function(dr) {
  var sim = this.lc.sim;
  // check if update is necessary (call without parameter to force update)
  this.dr += 2.0*dr;
  if( this.dr < sim.rp ) {
    return;
  }
  this.dr = 0.0;

  // update Linkcells first
  this.lc.update()

  // clear Neigborlist
  this.clear()

  var i,j,k,i2,j2,k2,n,l,m,
      ka, la, ll,
      // half the neighbors
      neigh = [
        [1,0,0], [0,1,0], [0,0,1], [1,1,0], [1,0,1], [0,1,1], [1,1,1],
        [this.lc.nx-1,1,0], [1,0,this.lc.nz-1], [0,this.lc.ny-1,1],
        [this.lc.nx-1,1,1], [1,this.lc.ny-1,1],[ 1,1,this.lc.nz-1]
      ];

  // loop over all cells
  for( i = 0; i < this.lc.nx; ++i ) {
    for( j = 0; j < this.lc.ny; ++j ) {
      for( k = 0; k < this.lc.nz; ++k ) {

        // loop over all atoms in local cell
        ll = this.lc.data[i][j][k].length;
        for( n = 0; n < ll; ++n ) {
          // loop over remaining atoms in local cell
          ka = this.lc.data[i][j][k][n];
          for( l = n+1; l < ll; ++l ) {
            la = this.lc.data[i][j][k][l];
            if( Vector3d.pbcdistance2( sim.atoms[ka].p, sim.atoms[la].p, sim.ss ) < this.lc.rm2 ) {
              this.data[ka].push(la);
            }
          }

          // loop over half the neighbor cells
          for( m = 0; m < neigh.length; ++m ) {
            i2 = (i+neigh[m][0]) % this.lc.nx;
            j2 = (j+neigh[m][1]) % this.lc.ny;
            k2 = (k+neigh[m][2]) % this.lc.nz;
            // loop over all atoms in those neighbor cells
            for( l = 0; l < this.lc.data[i2][j2][k2].length; ++l ) {
              la = this.lc.data[i2][j2][k2][l];
              if( Vector3d.pbcdistance2( sim.atoms[ka].p, sim.atoms[la].p, sim.ss ) < this.lc.rm2 ) {
                this.data[ka].push(la);
              }
            }
          }
        }
        // end local cell

      }
    }
  }

  // increase update counter
  this.count++;
}
<commit_msg>initial step to toggle PBC in neighborlist generation<commit_after>function Neighborlist3d(lc) {
  this.lc = lc;
  this.dr = 0.0;
  this.data = [];
  this.count = 0;
  this.pbc = true;
}

Neighborlist3d.prototype.clear = function() {
  for( var i = 0; i < this.lc.sim.atoms.length; ++i ) {
    this.data[i] = [];
  }
}
Neighborlist3d.prototype.update = function(dr) {
  var sim = this.lc.sim;
  // check if update is necessary (call without parameter to force update)
  this.dr += 2.0*dr;
  if( this.dr < sim.rp ) {
    return;
  }
  this.dr = 0.0;

  // update Linkcells first
  this.lc.update()

  // clear Neigborlist
  this.clear()

  var i,j,k,i2,j2,k2,n,l,m,
      ka, la, ll,
      // half the neighbors
      neigh = [
        [1,0,0], [0,1,0], [0,0,1], [1,1,0], [1,0,1], [0,1,1], [1,1,1],
        [this.lc.nx-1,1,0], [1,0,this.lc.nz-1], [0,this.lc.ny-1,1],
        [this.lc.nx-1,1,1], [1,this.lc.ny-1,1],[ 1,1,this.lc.nz-1]
      ];

  // loop over all cells
  for( i = 0; i < this.lc.nx; ++i ) {
    for( j = 0; j < this.lc.ny; ++j ) {
      for( k = 0; k < this.lc.nz; ++k ) {

        // loop over all atoms in local cell
        ll = this.lc.data[i][j][k].length;
        for( n = 0; n < ll; ++n ) {
          // loop over remaining atoms in local cell
          ka = this.lc.data[i][j][k][n];
          for( l = n+1; l < ll; ++l ) {
            la = this.lc.data[i][j][k][l];
            if( Vector3d.pbcdistance2( sim.atoms[ka].p, sim.atoms[la].p, sim.ss ) < this.lc.rm2 ) {
              this.data[ka].push(la);
            }
          }

          // loop over half the neighbor cells
          for( m = 0; m < neigh.length; ++m ) {
            i2 = (i+neigh[m][0]);
            if( this.pbc ) {
	      i2 %= this.lc.nx;
	    } else if( i2 >= this.lc.nx ) continue;

            j2 = (j+neigh[m][1]);
            if( this.pbc ) {
              j2 %= this.lc.ny;
	    } else if( j2 >= this.lc.ny ) continue;

            k2 = (k+neigh[m][2]);
            if( this.pbc ) {
              k2 %= this.lc.nz;
	    } else if( k2 >= this.lc.nz ) continue;

            // loop over all atoms in those neighbor cells
            for( l = 0; l < this.lc.data[i2][j2][k2].length; ++l ) {
              la = this.lc.data[i2][j2][k2][l];
              if( Vector3d.pbcdistance2( sim.atoms[ka].p, sim.atoms[la].p, sim.ss ) < this.lc.rm2 ) {
                this.data[ka].push(la);
              }
            }
          }
        }
        // end local cell

      }
    }
  }

  // increase update counter
  this.count++;
}
<|endoftext|>


<commit_before>function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.projectId = $("#projectHeader").data('value');
    gvm.lastIdFromDb = -1;
    gvm.lastId = -1;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);
    gvm.projectCompletenessTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectCompletenessTitle");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + gvm.projectId, function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
            console.log(data);
        });
    };

    gvm.documents = ko.observableArray([]);

    gvm.addDocument = function(id, description, amount_required, weight) {
        var document = {id: id, description: description, amount_required: amount_required, weight: weight};
        gvm.documents.push(document);

        $('#removebtn-' + id).bind('click', function(event, data) {
            gvm.removeDocument(id, document);
            event.stopPropagation();
        });
    };

    gvm.addDocumentToSubmit = function() {
        ++gvm.lastId;
        gvm.addDocument(gvm.lastId, "", "", "");
    };

    gvm.getDocumentsToSubmit = function() {
        $.getJSON('/api/project/'+ gvm.projectId + '/documents', function(data) {
            $.each(data, function(i, item) {
                gvm.addDocument(item.id, item.description, item.amount_required, item.weight);
                gvm.lastIdFromDb = item.id;
                gvm.lastId = item.id;
            });
        });
    };

    gvm.removeDocument = function(id, document) {
        if(id <= gvm.lastIdFromDb) {
            $.ajax({
                url: "/api/delete/document/" + id,
                type: "DELETE",
                success: function() {
                    gvm.documents.remove(document);
                }
            });
        } else {
            gvm.documents.remove(document);
        }
    };

    gvm.saveDocumentsToSubmit = function() {
        console.log("/api/project/" + gvm.projectId + "/documents/" + gvm.lastId);
        console.log(ko.toJS(gvm.documents));
        var json = JSON.stringify(ko.toJS(gvm.documents));
        $.ajax({
            url: "/api/project/" + gvm.projectId + "/documents/" + gvm.lastIdFromDb,
            type: "POST",
            datatype: "json",
            data: {documents: json},
            success: function() {
                console.log("success");
                location.reload();
            },
            error: function() {
                console.log("error");
            }
        });
    };
}

function initPage() {
    viewModel.getProjectInfo();
    viewModel.getDocumentsToSubmit();
}<commit_msg>documents<commit_after>function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.projectId = $("#projectHeader").data('value');
    gvm.lastIdFromDb = -1;
    gvm.lastId = -1;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);
    gvm.projectCompletenessTitle = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectCompletenessTitle");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + gvm.projectId, function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
            console.log(data);
        });
    };

    gvm.documents = ko.observableArray([]);

    gvm.totalDocumentPercentage = 0;

    gvm.addDocument = function(id, description, amount_required, weight) {
        var document = {id: id, description: description, amount_required: amount_required, weight: weight};
        gvm.documents.push(document);

        $('#removebtn-' + id).bind('click', function(event, data) {
            gvm.removeDocument(id, document);
            event.stopPropagation();
        });
    };

    gvm.addDocumentToSubmit = function() {
        ++gvm.lastId;
        gvm.addDocument(gvm.lastId, "", "", "");
    };

    gvm.getDocumentsToSubmit = function() {
        $.getJSON('/api/project/'+ gvm.projectId + '/documents', function(data) {
            $.each(data, function(i, item) {
                gvm.addDocument(item.id, item.description, item.amount_required, item.weight);
                gvm.lastIdFromDb = item.id;
                gvm.lastId = item.id;
            });
        });
    };

    gvm.removeDocument = function(id, document) {
        if(id <= gvm.lastIdFromDb) {
            $.ajax({
                url: "/api/delete/document/" + id,
                type: "DELETE",
                success: function() {
                    gvm.documents.remove(document);
                }
            });
        } else {
            gvm.documents.remove(document);
        }
    };

    gvm.saveDocumentsToSubmit = function() {
        console.log("/api/project/" + gvm.projectId + "/documents/" + gvm.lastId);
        console.log(ko.toJS(gvm.documents));
        var json = JSON.stringify(ko.toJS(gvm.documents));
        $.ajax({
            url: "/api/project/" + gvm.projectId + "/documents/" + gvm.lastIdFromDb,
            type: "POST",
            datatype: "json",
            data: {documents: json},
            success: function() {
                console.log("success");
                location.reload();
            },
            error: function() {
                console.log("error");
            }
        });
    };
}

function initPage() {
    viewModel.getProjectInfo();
    viewModel.getDocumentsToSubmit();
}<|endoftext|>


<commit_before>import {
  getQueryStringParam, Component, DefaultDOMElement, parseKeyEvent,
  HttpStorageClient, VfsStorageClient, InMemoryDarBuffer
} from 'substance'
import {
  Texture, JATSImportDialog, TextureArchive
} from 'substance-texture'

window.addEventListener('load', () => {
  MyTextureEditor.mount({}, window.document.body)
})

/* Sample integration of Texture */
class MyTextureEditor extends Component {

  didMount() {
    this._init()
    DefaultDOMElement.getBrowserWindow().on('keydown', this._keyDown, this)
  }

  dispose() {
    DefaultDOMElement.getBrowserWindow().off(this)
  }

  getInitialState() {
    return {
      archive: undefined,
      error: undefined
    }
  }

  render($$) {
    let el = $$('div').addClass('sc-app')
    let archive = this.state.archive
    let err = this.state.error

    if (archive) {
      el.append(
        $$(Texture, {archive})
      )
    } else if (err) {
      if (err.type === 'jats-import-error') {
        el.append(
          $$(JATSImportDialog, { errors: err.detail })
        )
      } else {
        el.append(
          'ERROR:',
          err.message
        )
      }
    } else {
      // LOADING...
    }
    return el
  }

  _init() {
    let archiveId = getQueryStringParam('archive') || 'kitchen-sink'
    let storageType = getQueryStringParam('storage') || 'vfs'
    let storageUrl = getQueryStringParam('storageUrl') || '/archives'
    let storage
    if (storageType==='vfs') {
      storage = new VfsStorageClient(window.vfs, './data/')
    } else {
      storage = new HttpStorageClient(storageUrl)
    }
    let buffer = new InMemoryDarBuffer()
    let archive = new TextureArchive(storage, buffer)
    archive.load(archiveId)
    .then(() => {
      setTimeout(() => {
        this.setState({archive})
      }, 0)
    }).catch(error => {
      console.error(error)
      this.setState({error})
    })
  }

  /*
    We may want an explicit save button, that can be configured on app level,
    but passed down to editor toolbars.
  */
  _save() {
    this.state.archive.save().then(() => {
      console.info('successfully saved')
    }).catch(err => {
      console.error(err)
    })
  }

  _keyDown(e) {
    let key = parseKeyEvent(e)
    if (key === 'META+83') {
      this._save()
      e.preventDefault()
    }
  }
}
<commit_msg>Consider CTRL+S.<commit_after>import {
  getQueryStringParam, Component, DefaultDOMElement, parseKeyEvent,
  HttpStorageClient, VfsStorageClient, InMemoryDarBuffer
} from 'substance'
import {
  Texture, JATSImportDialog, TextureArchive
} from 'substance-texture'

window.addEventListener('load', () => {
  MyTextureEditor.mount({}, window.document.body)
})

/* Sample integration of Texture */
class MyTextureEditor extends Component {

  didMount() {
    this._init()
    DefaultDOMElement.getBrowserWindow().on('keydown', this._keyDown, this)
  }

  dispose() {
    DefaultDOMElement.getBrowserWindow().off(this)
  }

  getInitialState() {
    return {
      archive: undefined,
      error: undefined
    }
  }

  render($$) {
    let el = $$('div').addClass('sc-app')
    let archive = this.state.archive
    let err = this.state.error

    if (archive) {
      el.append(
        $$(Texture, {archive})
      )
    } else if (err) {
      if (err.type === 'jats-import-error') {
        el.append(
          $$(JATSImportDialog, { errors: err.detail })
        )
      } else {
        el.append(
          'ERROR:',
          err.message
        )
      }
    } else {
      // LOADING...
    }
    return el
  }

  _init() {
    let archiveId = getQueryStringParam('archive') || 'kitchen-sink'
    let storageType = getQueryStringParam('storage') || 'vfs'
    let storageUrl = getQueryStringParam('storageUrl') || '/archives'
    let storage
    if (storageType==='vfs') {
      storage = new VfsStorageClient(window.vfs, './data/')
    } else {
      storage = new HttpStorageClient(storageUrl)
    }
    let buffer = new InMemoryDarBuffer()
    let archive = new TextureArchive(storage, buffer)
    archive.load(archiveId)
    .then(() => {
      setTimeout(() => {
        this.setState({archive})
      }, 0)
    }).catch(error => {
      console.error(error)
      this.setState({error})
    })
  }

  /*
    We may want an explicit save button, that can be configured on app level,
    but passed down to editor toolbars.
  */
  _save() {
    this.state.archive.save().then(() => {
      console.info('successfully saved')
    }).catch(err => {
      console.error(err)
    })
  }

  _keyDown(e) {
    let key = parseKeyEvent(e)
    // CommandOrControl+S
    if (key === 'META+83' || key === 'CTRL+83') {
      this._save()
      e.preventDefault()
    }
  }
}
<|endoftext|>