<commit_before>/**
 * Module dependencies.
 */

var util = require('./util');
var transports = require('./transports');
var Emitter = require('emitter');
var debug = require('debug')('engine.io-client:socket');
var index = require('indexof');
var parser = require('engine.io-parser');
var parseuri = require('parseuri');
var parsejson = require('parsejson');
var parseqs = require('parseqs');

/**
 * Module exports.
 */

module.exports = Socket;

/**
 * Noop function.
 *
 * @api private
 */

function noop(){}

/**
 * Socket constructor.
 *
 * @param {String|Object} uri or options
 * @param {Object} options
 * @api public
 */

function Socket(uri, opts){
  if (!(this instanceof Socket)) return new Socket(uri, opts);

  opts = opts || {};

  if (uri && 'object' == typeof uri) {
    opts = uri;
    uri = null;
  }

  if (uri) {
    uri = parseuri(uri);
    opts.host = uri.host;
    opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
    opts.port = uri.port;
    if (uri.query) opts.query = uri.query;
  }

  this.secure = null != opts.secure ? opts.secure :
    (global.location && 'https:' == location.protocol);

  if (opts.host) {
    var pieces = opts.host.split(':');
    opts.hostname = pieces.shift();
    if (pieces.length) opts.port = pieces.pop();
  }

  this.agent = opts.agent || false;
  this.hostname = opts.hostname ||
    (global.location ? location.hostname : 'localhost');
  this.port = opts.port || (global.location && location.port ?
       location.port :
       (this.secure ? 443 : 80));
  this.query = opts.query || {};
  if ('string' == typeof this.query) this.query = parseqs.decode(this.query);
  this.upgrade = false !== opts.upgrade;
  this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
  this.forceJSONP = !!opts.forceJSONP;
  this.forceBase64 = !!opts.forceBase64;
  this.timestampParam = opts.timestampParam || 't';
  this.timestampRequests = opts.timestampRequests;
  this.flashPath = opts.flashPath || '';
  this.transports = opts.transports || ['polling', 'websocket', 'flashsocket'];
  this.readyState = '';
  this.writeBuffer = [];
  this.callbackBuffer = [];
  this.policyPort = opts.policyPort || 843;
  this.rememberUpgrade = opts.rememberUpgrade || false;
  this.open();
  this.binaryType = null;
  this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
}

Socket.priorWebsocketSuccess = false;

/**
 * Mix in `Emitter`.
 */

Emitter(Socket.prototype);

/**
 * Protocol version.
 *
 * @api public
 */

Socket.protocol = parser.protocol; // this is an int

/**
 * Expose deps for legacy compatibility
 * and standalone browser access.
 */

Socket.Socket = Socket;
Socket.Transport = require('./transport');
Socket.transports = require('./transports');
Socket.util = require('./util');
Socket.parser = require('engine.io-parser');

/**
 * Creates transport of the given type.
 *
 * @param {String} transport name
 * @return {Transport}
 * @api private
 */

Socket.prototype.createTransport = function (name) {
  debug('creating transport "%s"', name);
  var query = clone(this.query);

  // append engine.io protocol identifier
  query.EIO = parser.protocol;

  // transport name
  query.transport = name;

  // session id if we already have one
  if (this.id) query.sid = this.id;

  var transport = new transports[name]({
    agent: this.agent,
    hostname: this.hostname,
    port: this.port,
    secure: this.secure,
    path: this.path,
    query: query,
    forceJSONP: this.forceJSONP,
    forceBase64: this.forceBase64,
    timestampRequests: this.timestampRequests,
    timestampParam: this.timestampParam,
    flashPath: this.flashPath,
    policyPort: this.policyPort,
    socket: this
  });

  return transport;
};

function clone (obj) {
  var o = {};
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      o[i] = obj[i];
    }
  }
  return o;
}

/**
 * Initializes transport to use and starts probe.
 *
 * @api private
 */
Socket.prototype.open = function () {
  var transport;
  if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) {
    transport = 'websocket';
  } else {
    transport = this.transports[0];
  }
  this.readyState = 'opening';
  var transport = this.createTransport(transport);
  transport.open();
  this.setTransport(transport);
};

/**
 * Sets the current transport. Disables the existing one (if any).
 *
 * @api private
 */

Socket.prototype.setTransport = function(transport){
  debug('setting transport %s', transport.name);
  var self = this;

  if (this.transport) {
    debug('clearing existing transport %s', this.transport.name);
    this.transport.removeAllListeners();
  }

  // set up transport
  this.transport = transport;

  // set up transport listeners
  transport
  .on('drain', function(){
    self.onDrain();
  })
  .on('packet', function(packet){
    self.onPacket(packet);
  })
  .on('error', function(e){
    self.onError(e);
  })
  .on('close', function(){
    self.onClose('transport close');
  });
};

/**
 * Probes a transport.
 *
 * @param {String} transport name
 * @api private
 */

Socket.prototype.probe = function (name) {
  debug('probing transport "%s"', name);
  var transport = this.createTransport(name, { probe: 1 })
    , failed = false
    , self = this;

  Socket.priorWebsocketSuccess = false;

  transport.once('open', function () {
    if (self.onlyBinaryUpgrades) {
      var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
      failed = failed || upgradeLosesBinary;
    }
    if (failed) return;

    debug('probe transport "%s" opened', name);
    transport.send([{ type: 'ping', data: 'probe' }]);
    transport.once('packet', function (msg) {
      if (failed) return;
      if ('pong' == msg.type && 'probe' == msg.data) {
        debug('probe transport "%s" pong', name);
        self.upgrading = true;
        self.emit('upgrading', transport);
        Socket.priorWebsocketSuccess = 'websocket' == transport.name;

        debug('pausing current transport "%s"', self.transport.name);
        self.transport.pause(function () {
          if (failed) return;
          if ('closed' == self.readyState || 'closing' == self.readyState) {
            return;
          }
          debug('changing transport and sending upgrade packet');
          transport.removeListener('error', onerror);
          self.setTransport(transport);
          transport.send([{ type: 'upgrade' }]);
          self.emit('upgrade', transport);
          transport = null;
          self.upgrading = false;
          self.flush();
        });
      } else {
        debug('probe transport "%s" failed', name);
        var err = new Error('probe error');
        err.transport = transport.name;
        self.emit('upgradeError', err);
      }
    });
  });

  transport.once('error', onerror);
  function onerror(err) {
    if (failed) return;

    // Any callback called by transport should be ignored since now
    failed = true;

    var error = new Error('probe error: ' + err);
    error.transport = transport.name;

    transport.close();
    transport = null;

    debug('probe transport "%s" failed because of error: %s', name, err);

    self.emit('upgradeError', error);
  }

  transport.open();

  this.once('close', function () {
    if (transport) {
      debug('socket closed prematurely - aborting probe');
      failed = true;
      transport.close();
      transport = null;
    }
  });

  this.once('upgrading', function (to) {
    if (transport && to.name != transport.name) {
      debug('"%s" works - aborting "%s"', to.name, transport.name);
      transport.close();
      transport = null;
    }
  });
};

/**
 * Called when connection is deemed open.
 *
 * @api public
 */

Socket.prototype.onOpen = function () {
  debug('socket open');
  this.readyState = 'open';
  Socket.priorWebsocketSuccess = 'websocket' == this.transport.name;
  this.emit('open');
  this.onopen && this.onopen.call(this);
  this.flush();

  // we check for `readyState` in case an `open`
  // listener already closed the socket
  if ('open' == this.readyState && this.upgrade && this.transport.pause) {
    debug('starting upgrade probes');
    for (var i = 0, l = this.upgrades.length; i < l; i++) {
      this.probe(this.upgrades[i]);
    }
  }
};

/**
 * Handles a packet.
 *
 * @api private
 */

Socket.prototype.onPacket = function (packet) {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    debug('socket receive: type "%s", data "%s"', packet.type, packet.data);

    this.emit('packet', packet);

    // Socket is live - any packet counts
    this.emit('heartbeat');

    switch (packet.type) {
      case 'open':
        this.onHandshake(parsejson(packet.data));
        break;

      case 'pong':
        this.setPing();
        break;

      case 'error':
        var err = new Error('server error');
        err.code = packet.data;
        this.emit('error', err);
        break;

      case 'message':
        this.emit('data', packet.data);
        this.emit('message', packet.data);
        break;
    }
  } else {
    debug('packet received with socket readyState "%s"', this.readyState);
  }
};

/**
 * Called upon handshake completion.
 *
 * @param {Object} handshake obj
 * @api private
 */

Socket.prototype.onHandshake = function (data) {
  this.emit('handshake', data);
  this.id = data.sid;
  this.transport.query.sid = data.sid;
  this.upgrades = this.filterUpgrades(data.upgrades);
  this.pingInterval = data.pingInterval;
  this.pingTimeout = data.pingTimeout;
  this.onOpen();
  this.setPing();

  // Prolong liveness of socket on heartbeat
  this.removeListener('heartbeat', this.onHeartbeat);
  this.on('heartbeat', this.onHeartbeat);
};

/**
 * Resets ping timeout.
 *
 * @api private
 */

Socket.prototype.onHeartbeat = function (timeout) {
  clearTimeout(this.pingTimeoutTimer);
  var self = this;
  self.pingTimeoutTimer = setTimeout(function () {
    if ('closed' == self.readyState) return;
    self.onClose('ping timeout');
  }, timeout || (self.pingInterval + self.pingTimeout));
};

/**
 * Pings server every `this.pingInterval` and expects response
 * within `this.pingTimeout` or closes connection.
 *
 * @api private
 */

Socket.prototype.setPing = function () {
  var self = this;
  clearTimeout(self.pingIntervalTimer);
  self.pingIntervalTimer = setTimeout(function () {
    debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
    self.ping();
    self.onHeartbeat(self.pingTimeout);
  }, self.pingInterval);
};

/**
* Sends a ping packet.
*
* @api public
*/

Socket.prototype.ping = function () {
  this.sendPacket('ping');
};

/**
 * Called on `drain` event
 *
 * @api private
 */

Socket.prototype.onDrain = function() {
  for (var i = 0; i < this.prevBufferLen; i++) {
    if (this.callbackBuffer[i]) {
      this.callbackBuffer[i]();
    }
  }

  this.writeBuffer.splice(0, this.prevBufferLen);
  this.callbackBuffer.splice(0, this.prevBufferLen);

  // setting prevBufferLen = 0 is very important
  // for example, when upgrading, upgrade packet is sent over,
  // and a nonzero prevBufferLen could cause problems on `drain`
  this.prevBufferLen = 0;

  if (this.writeBuffer.length == 0) {
    this.emit('drain');
  } else {
    this.flush();
  }
};

/**
 * Flush write buffers.
 *
 * @api private
 */

Socket.prototype.flush = function () {
  if ('closed' != this.readyState && this.transport.writable &&
    !this.upgrading && this.writeBuffer.length) {
    debug('flushing %d packets in socket', this.writeBuffer.length);
    this.transport.send(this.writeBuffer);
    // keep track of current length of writeBuffer
    // splice writeBuffer and callbackBuffer on `drain`
    this.prevBufferLen = this.writeBuffer.length;
    this.emit('flush');
  }
};

/**
 * Sends a message.
 *
 * @param {String} message.
 * @param {Function} callback function.
 * @return {Socket} for chaining.
 * @api public
 */

Socket.prototype.write =
Socket.prototype.send = function (msg, fn) {
  this.sendPacket('message', msg, fn);
  return this;
};

/**
 * Sends a packet.
 *
 * @param {String} packet type.
 * @param {String} data.
 * @param {Function} callback function.
 * @api private
 */

Socket.prototype.sendPacket = function (type, data, fn) {
  var packet = { type: type, data: data };
  this.emit('packetCreate', packet);
  this.writeBuffer.push(packet);
  this.callbackBuffer.push(fn);
  this.flush();
};

/**
 * Closes the connection.
 *
 * @api private
 */

Socket.prototype.close = function () {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    this.onClose('forced close');
    debug('socket closing - telling transport to close');
    this.transport.close();
  }

  return this;
};

/**
 * Called upon transport error
 *
 * @api private
 */

Socket.prototype.onError = function (err) {
  debug('socket error %j', err);
  Socket.priorWebsocketSuccess = false;
  this.emit('error', err);
  this.onClose('transport error', err);
};

/**
 * Called upon transport close.
 *
 * @api private
 */

Socket.prototype.onClose = function (reason, desc) {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    debug('socket close with reason: "%s"', reason);
    var self = this;

    // clear timers
    clearTimeout(this.pingIntervalTimer);
    clearTimeout(this.pingTimeoutTimer);

    // clean buffers in next tick, so developers can still
    // grab the buffers on `close` event
    setTimeout(function() {
      self.writeBuffer = [];
      self.callbackBuffer = [];
      self.prevBufferLen = 0;
    }, 0);

    // stop event from firing again for transport
    this.transport.removeAllListeners('close');

    // ensure transport won't stay open
    this.transport.close();

    // ignore further transport communication
    this.transport.removeAllListeners();

    // set ready state
    this.readyState = 'closed';

    // clear session id
    this.id = null;

    // emit close event
    this.emit('close', reason, desc);
  }
};

/**
 * Filters upgrades, returning only those matching client transports.
 *
 * @param {Array} server upgrades
 * @api private
 *
 */

Socket.prototype.filterUpgrades = function (upgrades) {
  var filteredUpgrades = [];
  for (var i = 0, j = upgrades.length; i<j; i++) {
    if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
  }
  return filteredUpgrades;
};
<commit_msg>Clean up probing and handle premature transport disconnect<commit_after>/**
 * Module dependencies.
 */

var util = require('./util');
var transports = require('./transports');
var Emitter = require('emitter');
var debug = require('debug')('engine.io-client:socket');
var index = require('indexof');
var parser = require('engine.io-parser');
var parseuri = require('parseuri');
var parsejson = require('parsejson');
var parseqs = require('parseqs');

/**
 * Module exports.
 */

module.exports = Socket;

/**
 * Noop function.
 *
 * @api private
 */

function noop(){}

/**
 * Socket constructor.
 *
 * @param {String|Object} uri or options
 * @param {Object} options
 * @api public
 */

function Socket(uri, opts){
  if (!(this instanceof Socket)) return new Socket(uri, opts);

  opts = opts || {};

  if (uri && 'object' == typeof uri) {
    opts = uri;
    uri = null;
  }

  if (uri) {
    uri = parseuri(uri);
    opts.host = uri.host;
    opts.secure = uri.protocol == 'https' || uri.protocol == 'wss';
    opts.port = uri.port;
    if (uri.query) opts.query = uri.query;
  }

  this.secure = null != opts.secure ? opts.secure :
    (global.location && 'https:' == location.protocol);

  if (opts.host) {
    var pieces = opts.host.split(':');
    opts.hostname = pieces.shift();
    if (pieces.length) opts.port = pieces.pop();
  }

  this.agent = opts.agent || false;
  this.hostname = opts.hostname ||
    (global.location ? location.hostname : 'localhost');
  this.port = opts.port || (global.location && location.port ?
       location.port :
       (this.secure ? 443 : 80));
  this.query = opts.query || {};
  if ('string' == typeof this.query) this.query = parseqs.decode(this.query);
  this.upgrade = false !== opts.upgrade;
  this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
  this.forceJSONP = !!opts.forceJSONP;
  this.forceBase64 = !!opts.forceBase64;
  this.timestampParam = opts.timestampParam || 't';
  this.timestampRequests = opts.timestampRequests;
  this.flashPath = opts.flashPath || '';
  this.transports = opts.transports || ['polling', 'websocket', 'flashsocket'];
  this.readyState = '';
  this.writeBuffer = [];
  this.callbackBuffer = [];
  this.policyPort = opts.policyPort || 843;
  this.rememberUpgrade = opts.rememberUpgrade || false;
  this.open();
  this.binaryType = null;
  this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
}

Socket.priorWebsocketSuccess = false;

/**
 * Mix in `Emitter`.
 */

Emitter(Socket.prototype);

/**
 * Protocol version.
 *
 * @api public
 */

Socket.protocol = parser.protocol; // this is an int

/**
 * Expose deps for legacy compatibility
 * and standalone browser access.
 */

Socket.Socket = Socket;
Socket.Transport = require('./transport');
Socket.transports = require('./transports');
Socket.util = require('./util');
Socket.parser = require('engine.io-parser');

/**
 * Creates transport of the given type.
 *
 * @param {String} transport name
 * @return {Transport}
 * @api private
 */

Socket.prototype.createTransport = function (name) {
  debug('creating transport "%s"', name);
  var query = clone(this.query);

  // append engine.io protocol identifier
  query.EIO = parser.protocol;

  // transport name
  query.transport = name;

  // session id if we already have one
  if (this.id) query.sid = this.id;

  var transport = new transports[name]({
    agent: this.agent,
    hostname: this.hostname,
    port: this.port,
    secure: this.secure,
    path: this.path,
    query: query,
    forceJSONP: this.forceJSONP,
    forceBase64: this.forceBase64,
    timestampRequests: this.timestampRequests,
    timestampParam: this.timestampParam,
    flashPath: this.flashPath,
    policyPort: this.policyPort,
    socket: this
  });

  return transport;
};

function clone (obj) {
  var o = {};
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      o[i] = obj[i];
    }
  }
  return o;
}

/**
 * Initializes transport to use and starts probe.
 *
 * @api private
 */
Socket.prototype.open = function () {
  var transport;
  if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) {
    transport = 'websocket';
  } else {
    transport = this.transports[0];
  }
  this.readyState = 'opening';
  var transport = this.createTransport(transport);
  transport.open();
  this.setTransport(transport);
};

/**
 * Sets the current transport. Disables the existing one (if any).
 *
 * @api private
 */

Socket.prototype.setTransport = function(transport){
  debug('setting transport %s', transport.name);
  var self = this;

  if (this.transport) {
    debug('clearing existing transport %s', this.transport.name);
    this.transport.removeAllListeners();
  }

  // set up transport
  this.transport = transport;

  // set up transport listeners
  transport
  .on('drain', function(){
    self.onDrain();
  })
  .on('packet', function(packet){
    self.onPacket(packet);
  })
  .on('error', function(e){
    self.onError(e);
  })
  .on('close', function(){
    self.onClose('transport close');
  });
};

/**
 * Probes a transport.
 *
 * @param {String} transport name
 * @api private
 */

Socket.prototype.probe = function (name) {
  debug('probing transport "%s"', name);
  var transport = this.createTransport(name, { probe: 1 })
    , failed = false
    , self = this;

  Socket.priorWebsocketSuccess = false;

  function onTransportOpen(){
    if (self.onlyBinaryUpgrades) {
      var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
      failed = failed || upgradeLosesBinary;
    }
    if (failed) return;

    debug('probe transport "%s" opened', name);
    transport.send([{ type: 'ping', data: 'probe' }]);
    transport.once('packet', function (msg) {
      if (failed) return;
      if ('pong' == msg.type && 'probe' == msg.data) {
        debug('probe transport "%s" pong', name);
        self.upgrading = true;
        self.emit('upgrading', transport);
        Socket.priorWebsocketSuccess = 'websocket' == transport.name;

        debug('pausing current transport "%s"', self.transport.name);
        self.transport.pause(function () {
          if (failed) return;
          if ('closed' == self.readyState || 'closing' == self.readyState) {
            return;
          }
          debug('changing transport and sending upgrade packet');

          removeAllListeners();

          self.setTransport(transport);
          transport.send([{ type: 'upgrade' }]);
          self.emit('upgrade', transport);
          transport = null;
          self.upgrading = false;
          self.flush();
        });
      } else {
        debug('probe transport "%s" failed', name);
        var err = new Error('probe error');
        err.transport = transport.name;
        self.emit('upgradeError', err);
      }
    });
  }

  //Handle any error that happens while probing
  function onerror(err) {
    if (failed) return;

    // Any callback called by transport should be ignored since now
    failed = true;

    var error = new Error('probe error: ' + err);
    error.transport = transport.name;

    removeAllListeners();

    transport.close();
    transport = null;

    debug('probe transport "%s" failed because of error: %s', name, err);

    self.emit('upgradeError', error);
  }

  function onTransportClose(){
    onerror("transport closed");
  }

  //When the socket is closed while we're probing
  function onclose(){
      onerror("socket closed");
  }

  //When the socket is upgraded while we're probing
  function onupgrade(to){
    if (transport && to.name != transport.name) {
      debug('"%s" works - aborting "%s"', to.name, transport.name);
      onerror("other transport works");
    }
  }

  //Remove all listeners on the transport and on self
  function removeAllListeners(){
    transport.removeListener('open', onTransportOpen);
    transport.removeListener('error', onerror);
    transport.removeListener('close', onTransportClose);
    self.removeListener('close', onclose);
    self.removeListener('upgrading', onupgrade);
  }

  transport.once('open', onTransportOpen);
  transport.once('error', onerror);

  this.once('close', onclose);
  this.once('upgrading', onupgrade);

  transport.open();

};

/**
 * Called when connection is deemed open.
 *
 * @api public
 */

Socket.prototype.onOpen = function () {
  debug('socket open');
  this.readyState = 'open';
  Socket.priorWebsocketSuccess = 'websocket' == this.transport.name;
  this.emit('open');
  this.onopen && this.onopen.call(this);
  this.flush();

  // we check for `readyState` in case an `open`
  // listener already closed the socket
  if ('open' == this.readyState && this.upgrade && this.transport.pause) {
    debug('starting upgrade probes');
    for (var i = 0, l = this.upgrades.length; i < l; i++) {
      this.probe(this.upgrades[i]);
    }
  }
};

/**
 * Handles a packet.
 *
 * @api private
 */

Socket.prototype.onPacket = function (packet) {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    debug('socket receive: type "%s", data "%s"', packet.type, packet.data);

    this.emit('packet', packet);

    // Socket is live - any packet counts
    this.emit('heartbeat');

    switch (packet.type) {
      case 'open':
        this.onHandshake(parsejson(packet.data));
        break;

      case 'pong':
        this.setPing();
        break;

      case 'error':
        var err = new Error('server error');
        err.code = packet.data;
        this.emit('error', err);
        break;

      case 'message':
        this.emit('data', packet.data);
        this.emit('message', packet.data);
        break;
    }
  } else {
    debug('packet received with socket readyState "%s"', this.readyState);
  }
};

/**
 * Called upon handshake completion.
 *
 * @param {Object} handshake obj
 * @api private
 */

Socket.prototype.onHandshake = function (data) {
  this.emit('handshake', data);
  this.id = data.sid;
  this.transport.query.sid = data.sid;
  this.upgrades = this.filterUpgrades(data.upgrades);
  this.pingInterval = data.pingInterval;
  this.pingTimeout = data.pingTimeout;
  this.onOpen();
  this.setPing();

  // Prolong liveness of socket on heartbeat
  this.removeListener('heartbeat', this.onHeartbeat);
  this.on('heartbeat', this.onHeartbeat);
};

/**
 * Resets ping timeout.
 *
 * @api private
 */

Socket.prototype.onHeartbeat = function (timeout) {
  clearTimeout(this.pingTimeoutTimer);
  var self = this;
  self.pingTimeoutTimer = setTimeout(function () {
    if ('closed' == self.readyState) return;
    self.onClose('ping timeout');
  }, timeout || (self.pingInterval + self.pingTimeout));
};

/**
 * Pings server every `this.pingInterval` and expects response
 * within `this.pingTimeout` or closes connection.
 *
 * @api private
 */

Socket.prototype.setPing = function () {
  var self = this;
  clearTimeout(self.pingIntervalTimer);
  self.pingIntervalTimer = setTimeout(function () {
    debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
    self.ping();
    self.onHeartbeat(self.pingTimeout);
  }, self.pingInterval);
};

/**
* Sends a ping packet.
*
* @api public
*/

Socket.prototype.ping = function () {
  this.sendPacket('ping');
};

/**
 * Called on `drain` event
 *
 * @api private
 */

Socket.prototype.onDrain = function() {
  for (var i = 0; i < this.prevBufferLen; i++) {
    if (this.callbackBuffer[i]) {
      this.callbackBuffer[i]();
    }
  }

  this.writeBuffer.splice(0, this.prevBufferLen);
  this.callbackBuffer.splice(0, this.prevBufferLen);

  // setting prevBufferLen = 0 is very important
  // for example, when upgrading, upgrade packet is sent over,
  // and a nonzero prevBufferLen could cause problems on `drain`
  this.prevBufferLen = 0;

  if (this.writeBuffer.length == 0) {
    this.emit('drain');
  } else {
    this.flush();
  }
};

/**
 * Flush write buffers.
 *
 * @api private
 */

Socket.prototype.flush = function () {
  if ('closed' != this.readyState && this.transport.writable &&
    !this.upgrading && this.writeBuffer.length) {
    debug('flushing %d packets in socket', this.writeBuffer.length);
    this.transport.send(this.writeBuffer);
    // keep track of current length of writeBuffer
    // splice writeBuffer and callbackBuffer on `drain`
    this.prevBufferLen = this.writeBuffer.length;
    this.emit('flush');
  }
};

/**
 * Sends a message.
 *
 * @param {String} message.
 * @param {Function} callback function.
 * @return {Socket} for chaining.
 * @api public
 */

Socket.prototype.write =
Socket.prototype.send = function (msg, fn) {
  this.sendPacket('message', msg, fn);
  return this;
};

/**
 * Sends a packet.
 *
 * @param {String} packet type.
 * @param {String} data.
 * @param {Function} callback function.
 * @api private
 */

Socket.prototype.sendPacket = function (type, data, fn) {
  var packet = { type: type, data: data };
  this.emit('packetCreate', packet);
  this.writeBuffer.push(packet);
  this.callbackBuffer.push(fn);
  this.flush();
};

/**
 * Closes the connection.
 *
 * @api private
 */

Socket.prototype.close = function () {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    this.onClose('forced close');
    debug('socket closing - telling transport to close');
    this.transport.close();
  }

  return this;
};

/**
 * Called upon transport error
 *
 * @api private
 */

Socket.prototype.onError = function (err) {
  debug('socket error %j', err);
  Socket.priorWebsocketSuccess = false;
  this.emit('error', err);
  this.onClose('transport error', err);
};

/**
 * Called upon transport close.
 *
 * @api private
 */

Socket.prototype.onClose = function (reason, desc) {
  if ('opening' == this.readyState || 'open' == this.readyState) {
    debug('socket close with reason: "%s"', reason);
    var self = this;

    // clear timers
    clearTimeout(this.pingIntervalTimer);
    clearTimeout(this.pingTimeoutTimer);

    // clean buffers in next tick, so developers can still
    // grab the buffers on `close` event
    setTimeout(function() {
      self.writeBuffer = [];
      self.callbackBuffer = [];
      self.prevBufferLen = 0;
    }, 0);

    // stop event from firing again for transport
    this.transport.removeAllListeners('close');

    // ensure transport won't stay open
    this.transport.close();

    // ignore further transport communication
    this.transport.removeAllListeners();

    // set ready state
    this.readyState = 'closed';

    // clear session id
    this.id = null;

    // emit close event
    this.emit('close', reason, desc);
  }
};

/**
 * Filters upgrades, returning only those matching client transports.
 *
 * @param {Array} server upgrades
 * @api private
 *
 */

Socket.prototype.filterUpgrades = function (upgrades) {
  var filteredUpgrades = [];
  for (var i = 0, j = upgrades.length; i<j; i++) {
    if (~index(this.transports, upgrades[i])) filteredUpgrades.push(upgrades[i]);
  }
  return filteredUpgrades;
};
<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');
var express = require('express');
var fs = require('fs');
var config = require('./webpack.config');

var app = express();
var compiler = webpack(config);

app.use(require('webpack-dev-middleware')(compiler, {
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));
app.use(express.static('public'));

app.get('/api/assets/', function (req, res) {
  const svgPath = 'public/svg/';
  const svgFolders = [
    {
      id: 'Beards',
      sortOrder: 4
    },
    {
      id: 'Body',
      sortOrder: 0
    },
    {
      id: 'Glasses',
      sortOrder: 1
    },
    {
      id: 'Hairstyles',
      sortOrder: 1
    },
    {
      id: 'Scarfes',
      sortOrder: 3
    },
    {
      id: 'Shirts',
      sortOrder: 1
    },
    {
      id: 'Tie',
      sortOrder: 2
    }
  ];
  const data = svgFolders.map(folder => Object.assign({}, folder,
    { files: fs.readdirSync(path.join(svgPath, folder.id)) }
  ));
  res.setHeader('Content-Type', 'application/json');
  res.send(JSON.stringify(data));
});

app.listen(3000, function(err) {
  if (err) {
    return console.error(err);
  }

  console.log('Listening at http://localhost:3000/');
});
<commit_msg>Read sub folders for colors<commit_after>var path = require('path');
var webpack = require('webpack');
var express = require('express');
var fs = require('fs');
var config = require('./webpack.config');

var app = express();
var compiler = webpack(config);

app.use(require('webpack-dev-middleware')(compiler, {
  publicPath: config.output.publicPath
}));

app.use(require('webpack-hot-middleware')(compiler));
app.use(express.static('public'));

app.get('/api/assets/', function (req, res) {
  const svgPath = 'public/svg/';
  const svgFolders = [
    {
      id: 'Beards',
      colors: [
        {
          id: 'blond',
        },
        {
          id: 'brown-haired person',
        },
        {
          id: 'brunet',
        },
        {
          id: 'white'
        }
      ],
      sortOrder: 4
    },
    {
      id: 'Body',
      colors: [
        {
          id: 'default',
        }
      ],
      sortOrder: 0
    },
    {
      id: 'Glasses',
      colors: [
        {
          id: 'default',
        }
      ],
      sortOrder: 1
    },
    {
      id: 'Hairstyles',
      colors: [
        {
          id: 'blond',
        },
        {
          id: 'brown-haired person',
        },
        {
          id: 'brunet',
        },
        {
          id: 'white'
        }
      ],
      sortOrder: 1
    },
    {
      id: 'Scarfes',
      colors: [
        {
          id: 'default',
        }
      ],
      sortOrder: 3
    },
    {
      id: 'Shirts',
      colors: [
        {
          id: 'default',
        }
      ],
      sortOrder: 1
    },
    {
      id: 'Tie',
      colors: [
        {
          id: 'default',
        }
      ],
      sortOrder: 2
    }
  ];
  const data = svgFolders.map(folder => {
    const colors = folder.colors.map(color => {
      return Object.assign({}, color, {
        files: fs.readdirSync(path.join(svgPath, folder.id, color.id))
      });
    });

    return Object.assign({}, folder, {
      colors
    });
  });
  res.setHeader('Content-Type', 'application/json');
  res.send(JSON.stringify(data));
});

app.listen(3000, function(err) {
  if (err) {
    return console.error(err);
  }

  console.log('Listening at http://localhost:3000/');
});
<|endoftext|>


<commit_before>'use strict';

const gulp = require('gulp');
const serve = require('gulp-serve');
const babel = require('gulp-babel');
const concat = require('gulp-concat');
const clean = require('gulp-clean');
const polyfiller = require('gulp-polyfiller');

gulp.task('serve', ['build'], serve('build'));

gulp.task('build', ['copy', 'js']);

gulp.task('clean', () => {
  return gulp.src('build/*', {read: false})
    .pipe(clean());
});

gulp.task('js', ['polyfills'], () => {
  return gulp.src(['src/js/*.js', '!src/js/*.min.js'])
    .pipe(babel())
    .pipe(concat("app.js"))
    .pipe(gulp.dest('build/js'));
});

gulp.task('polyfills', () => {
  return polyfiller.bundle(['Promise', 'Fetch'])
    .pipe(gulp.dest('build/js'))
});

gulp.task('copy', () => {
  return gulp.src(['src/**/*', '!src/js/app.js'])
    .pipe(gulp.dest('build'));
});
<commit_msg>Add gulp watch command<commit_after>'use strict';

const gulp = require('gulp');
const serve = require('gulp-serve');
const babel = require('gulp-babel');
const concat = require('gulp-concat');
const clean = require('gulp-clean');
const polyfiller = require('gulp-polyfiller');

gulp.task('default', () => {
  console.log('Run `gulp watch` to start the development server');
});

gulp.task('watch', ['build', 'serve'], () => {
  gulp.watch('src/**/*', ['build']);
});

gulp.task('serve', serve('build'));

gulp.task('build', ['copy', 'js']);

gulp.task('clean', () => {
  return gulp.src('build/*', {read: false})
    .pipe(clean());
});

gulp.task('js', ['polyfills'], () => {
  return gulp.src(['src/js/*.js', '!src/js/*.min.js'])
    .pipe(babel())
    .pipe(concat("app.js"))
    .pipe(gulp.dest('build/js'));
});

gulp.task('polyfills', () => {
  return polyfiller.bundle(['Promise', 'Fetch'])
    .pipe(gulp.dest('build/js'))
});

gulp.task('copy', () => {
  return gulp.src(['src/**/*', '!src/js/app.js'])
    .pipe(gulp.dest('build'));
});
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:socket');

/**
 * Module exports.
 */

module.exports = exports = Socket;

/**
 * Blacklisted events.
 *
 * @api public
 */

exports.events = [
  'error',
  'connect',
  'disconnect',
  'newListener'
];

/**
 * Flags.
 *
 * @api public
 */

exports.flags = [
  'json',
  'volatile',
  'broadcast'
];

/**
 * `EventEmitter#emit` reference.
 */

var emit = Emitter.prototype.emit;

/**
 * Interface to a `Client` for a given `Namespace`.
 *
 * @param {Namespace} nsp
 * @param {Client} client
 * @api public
 */

function Socket(nsp, client){
  this.nsp = nsp;
  this.server = nsp.server;
  this.adapter = this.nsp.adapter;
  this.id = client.id;
  this.request = client.request;
  this.client = client;
  this.conn = client.conn;
  this.rooms = [];
  this.acks = {};
  this.connected = true;
  this.disconnected = false;
}

/**
 * Inherits from `EventEmitter`.
 */

Socket.prototype.__proto__ = Emitter.prototype;

/**
 * Apply flags from `Socket`.
 */

exports.flags.forEach(function(flag){
  Socket.prototype.__defineGetter__(flag, function(){
    this.flags = this.flags || {};
    this.flags[flag] = true;
    return this;
  });
});

/**
 * `request` engine.io shorcut.
 *
 * @api public
 */

Socket.prototype.__defineGetter__('request', function(){
  return this.conn.request;
});

/**
 * Emits to this client.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.emit = function(ev){
  if (~exports.events.indexOf(ev)) {
    emit.apply(this, arguments);
  } else {
    // set up packet object
    var args = Array.prototype.slice.call(arguments);
    var packet = { type: parser.EVENT, data: args };

    // access last argument to see if it's an ACK callback
    if ('function' == typeof args[args.length - 1]) {
      if (this._rooms || (this.flags && this.flags.broadcast)) {
        throw new Error('Callbacks are not supported when broadcasting');
      }

      debug('emitting packet with ack id %d', this.nsp.ids);
      this.acks[this.nsp.ids] = args.pop();
      packet.id = this.nsp.ids++;
    }

    if (this._rooms || (this.flags && this.flags.broadcast)) {
      this.adapter.broadcast(packet, {
        except: [this.id],
        rooms: this._rooms,
        flags: this.flags
      });
    } else {
      // dispatch packet
      this.packet(packet);
    }

    // reset flags
    delete this._rooms;
    delete this.flags;
  }
  return this;
};

/**
 * Targets a room when broadcasting.
 *
 * @param {String} name
 * @return {Socket} self
 * @api public
 */

Socket.prototype.to =
Socket.prototype.in = function(name){
  this._rooms = this._rooms || [];
  if (!~this._rooms.indexOf(name)) this._rooms.push(name);
  return this;
};

/**
 * Sends a `message` event.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.send =
Socket.prototype.write = function(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('message');
  this.emit.apply(this, args);
  return this;
};

/**
 * Writes a packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.packet = function(packet){
  packet.nsp = this.nsp.name;
  this.client.packet(packet);
};

/**
 * Joins a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.join = function(room, fn){
  debug('joining room %s', room);
  var self = this;
  if (~this.rooms.indexOf(room)) return this;
  this.adapter.add(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('joined room %s', room);
    self.rooms.push(room);
    fn && fn(null);
  });
  return this;
};

/**
 * Leaves a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.leave = function(room, fn){
  debug('leave room %s', room);
  var self = this;
  this.adapter.del(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('left room %s', room);
    self.rooms.splice(self.rooms.indexOf(room, 1));
    fn && fn(null);
  });
  return this;
};

/**
 * Leave all rooms.
 *
 * @api private
 */

Socket.prototype.leaveAll = function(){
  this.adapter.delAll(this.id);
};

/**
 * Called by `Namespace` upon succesful
 * middleware execution (ie: authorization).
 *
 * @api private
 */

Socket.prototype.onconnect = function(){
  debug('socket connected - writing packet');
  this.join(this.id);
  this.packet({ type: parser.CONNECT });
  this.nsp.connected[this.id] = this;
};

/**
 * Called with each packet. Called by `Client`.
 *
 * @param {Object} packet
 * @api private
 */

Socket.prototype.onpacket = function(packet){
  debug('got packet %j', packet);
  switch (packet.type) {
    case parser.EVENT:
      this.onevent(packet);
      break;

    case parser.ACK:
      this.onack(packet);
      break;

    case parser.DISCONNECT:
      this.ondisconnect();
      break;

    case parser.ERROR:
      this.emit('error', packet.data);
  }
};

/**
 * Called upon event packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.onevent = function(packet){
  var args = packet.data || [];
  debug('emitting event %j', args);

  if (null != packet.id) {
    debug('attaching ack callback to event');
    args.push(this.ack(packet.id));
  }

  emit.apply(this, args);
};

/**
 * Produces an ack callback to emit with an event.
 *
 * @param {Number} packet id
 * @api private
 */

Socket.prototype.ack = function(id){
  var self = this;
  var sent = false;
  return function(){
    // prevent double callbacks
    if (sent) return;
    var args = Array.prototype.slice.call(arguments);
    debug('sending ack %j', args);
    self.packet({
      id: id,
      type: parser.ACK,
      data: args
    });
  };
};

/**
 * Called upon ack packet.
 *
 * @api private
 */

Socket.prototype.onack = function(packet){
  var ack = this.acks[packet.id];
  if ('function' == typeof ack) {
    debug('calling ack %s with %j', packet.id, packet.data);
    ack.apply(this, packet.data);
    delete this.acks[packet.id];
  } else {
    debug('bad ack %s', packet.id);
  }
};

/**
 * Called upon client disconnect packet.
 *
 * @api private
 */

Socket.prototype.ondisconnect = function(){
  debug('got disconnect packet');
  this.onclose('server namespace disconnect');
};

/**
 * Called upon closing. Called by `Client`.
 *
 * @param {String} reason
 * @api private
 */

Socket.prototype.onclose = function(reason){
  if (!this.connected) return this;
  debug('closing socket - reason %s', reason);
  this.leaveAll();
  this.nsp.remove(this);
  this.client.remove(this);
  this.connected = false;
  this.disconnected = true;
  delete this.nsp.connected[this.id];
  this.emit('disconnect', reason);
};

/**
 * Produces an `error` packet.
 *
 * @param {Object} error object
 * @api private
 */

Socket.prototype.error = function(err){
  this.packet({ type: parser.ERROR, data: err });
};

/**
 * Disconnects this client.
 *
 * @param {Boolean} if `true`, closes the underlying connection
 * @return {Socket} self
 * @api public
 */

Socket.prototype.disconnect = function(close){
  if (!this.connected) return this;
  if (close) {
    this.client.disconnect();
  } else {
    this.packet({ type: parser.DISCONNECT });
    this.onclose('client namespace disconnect');
  }
  return this;
};
<commit_msg>socket: revert `onclose` reason<commit_after>
/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:socket');

/**
 * Module exports.
 */

module.exports = exports = Socket;

/**
 * Blacklisted events.
 *
 * @api public
 */

exports.events = [
  'error',
  'connect',
  'disconnect',
  'newListener'
];

/**
 * Flags.
 *
 * @api public
 */

exports.flags = [
  'json',
  'volatile',
  'broadcast'
];

/**
 * `EventEmitter#emit` reference.
 */

var emit = Emitter.prototype.emit;

/**
 * Interface to a `Client` for a given `Namespace`.
 *
 * @param {Namespace} nsp
 * @param {Client} client
 * @api public
 */

function Socket(nsp, client){
  this.nsp = nsp;
  this.server = nsp.server;
  this.adapter = this.nsp.adapter;
  this.id = client.id;
  this.request = client.request;
  this.client = client;
  this.conn = client.conn;
  this.rooms = [];
  this.acks = {};
  this.connected = true;
  this.disconnected = false;
}

/**
 * Inherits from `EventEmitter`.
 */

Socket.prototype.__proto__ = Emitter.prototype;

/**
 * Apply flags from `Socket`.
 */

exports.flags.forEach(function(flag){
  Socket.prototype.__defineGetter__(flag, function(){
    this.flags = this.flags || {};
    this.flags[flag] = true;
    return this;
  });
});

/**
 * `request` engine.io shorcut.
 *
 * @api public
 */

Socket.prototype.__defineGetter__('request', function(){
  return this.conn.request;
});

/**
 * Emits to this client.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.emit = function(ev){
  if (~exports.events.indexOf(ev)) {
    emit.apply(this, arguments);
  } else {
    // set up packet object
    var args = Array.prototype.slice.call(arguments);
    var packet = { type: parser.EVENT, data: args };

    // access last argument to see if it's an ACK callback
    if ('function' == typeof args[args.length - 1]) {
      if (this._rooms || (this.flags && this.flags.broadcast)) {
        throw new Error('Callbacks are not supported when broadcasting');
      }

      debug('emitting packet with ack id %d', this.nsp.ids);
      this.acks[this.nsp.ids] = args.pop();
      packet.id = this.nsp.ids++;
    }

    if (this._rooms || (this.flags && this.flags.broadcast)) {
      this.adapter.broadcast(packet, {
        except: [this.id],
        rooms: this._rooms,
        flags: this.flags
      });
    } else {
      // dispatch packet
      this.packet(packet);
    }

    // reset flags
    delete this._rooms;
    delete this.flags;
  }
  return this;
};

/**
 * Targets a room when broadcasting.
 *
 * @param {String} name
 * @return {Socket} self
 * @api public
 */

Socket.prototype.to =
Socket.prototype.in = function(name){
  this._rooms = this._rooms || [];
  if (!~this._rooms.indexOf(name)) this._rooms.push(name);
  return this;
};

/**
 * Sends a `message` event.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.send =
Socket.prototype.write = function(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('message');
  this.emit.apply(this, args);
  return this;
};

/**
 * Writes a packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.packet = function(packet){
  packet.nsp = this.nsp.name;
  this.client.packet(packet);
};

/**
 * Joins a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.join = function(room, fn){
  debug('joining room %s', room);
  var self = this;
  if (~this.rooms.indexOf(room)) return this;
  this.adapter.add(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('joined room %s', room);
    self.rooms.push(room);
    fn && fn(null);
  });
  return this;
};

/**
 * Leaves a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.leave = function(room, fn){
  debug('leave room %s', room);
  var self = this;
  this.adapter.del(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('left room %s', room);
    self.rooms.splice(self.rooms.indexOf(room, 1));
    fn && fn(null);
  });
  return this;
};

/**
 * Leave all rooms.
 *
 * @api private
 */

Socket.prototype.leaveAll = function(){
  this.adapter.delAll(this.id);
};

/**
 * Called by `Namespace` upon succesful
 * middleware execution (ie: authorization).
 *
 * @api private
 */

Socket.prototype.onconnect = function(){
  debug('socket connected - writing packet');
  this.join(this.id);
  this.packet({ type: parser.CONNECT });
  this.nsp.connected[this.id] = this;
};

/**
 * Called with each packet. Called by `Client`.
 *
 * @param {Object} packet
 * @api private
 */

Socket.prototype.onpacket = function(packet){
  debug('got packet %j', packet);
  switch (packet.type) {
    case parser.EVENT:
      this.onevent(packet);
      break;

    case parser.ACK:
      this.onack(packet);
      break;

    case parser.DISCONNECT:
      this.ondisconnect();
      break;

    case parser.ERROR:
      this.emit('error', packet.data);
  }
};

/**
 * Called upon event packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.onevent = function(packet){
  var args = packet.data || [];
  debug('emitting event %j', args);

  if (null != packet.id) {
    debug('attaching ack callback to event');
    args.push(this.ack(packet.id));
  }

  emit.apply(this, args);
};

/**
 * Produces an ack callback to emit with an event.
 *
 * @param {Number} packet id
 * @api private
 */

Socket.prototype.ack = function(id){
  var self = this;
  var sent = false;
  return function(){
    // prevent double callbacks
    if (sent) return;
    var args = Array.prototype.slice.call(arguments);
    debug('sending ack %j', args);
    self.packet({
      id: id,
      type: parser.ACK,
      data: args
    });
  };
};

/**
 * Called upon ack packet.
 *
 * @api private
 */

Socket.prototype.onack = function(packet){
  var ack = this.acks[packet.id];
  if ('function' == typeof ack) {
    debug('calling ack %s with %j', packet.id, packet.data);
    ack.apply(this, packet.data);
    delete this.acks[packet.id];
  } else {
    debug('bad ack %s', packet.id);
  }
};

/**
 * Called upon client disconnect packet.
 *
 * @api private
 */

Socket.prototype.ondisconnect = function(){
  debug('got disconnect packet');
  this.onclose('client namespace disconnect');
};

/**
 * Called upon closing. Called by `Client`.
 *
 * @param {String} reason
 * @api private
 */

Socket.prototype.onclose = function(reason){
  if (!this.connected) return this;
  debug('closing socket - reason %s', reason);
  this.leaveAll();
  this.nsp.remove(this);
  this.client.remove(this);
  this.connected = false;
  this.disconnected = true;
  delete this.nsp.connected[this.id];
  this.emit('disconnect', reason);
};

/**
 * Produces an `error` packet.
 *
 * @param {Object} error object
 * @api private
 */

Socket.prototype.error = function(err){
  this.packet({ type: parser.ERROR, data: err });
};

/**
 * Disconnects this client.
 *
 * @param {Boolean} if `true`, closes the underlying connection
 * @return {Socket} self
 * @api public
 */

Socket.prototype.disconnect = function(close){
  if (!this.connected) return this;
  if (close) {
    this.client.disconnect();
  } else {
    this.packet({ type: parser.DISCONNECT });
    this.onclose('server namespace disconnect');
  }
  return this;
};
<|endoftext|>


<commit_before>'use strict';

/*******************************************************************************
    DEPENDENCIES
*******************************************************************************/

var gulp = require('gulp');
var $ = require('gulp-load-plugins')();

var browserSync = require('browser-sync');
var reload = browserSync.reload;

var fs = require('fs');
var runSequence = require('run-sequence');
var pagespeed = require('psi');
var rsync = require('rsyncwrapper').rsync;
var pkg = require('./package.json');

var spritesmith = require('gulp.spritesmith');
var scsslint = require('gulp-scss-lint');
var combineMediaQueries = require('gulp-combine-media-queries');

var stylish = require('jshint-stylish');

var path = require('path');
var config = require('./config');
var folders = config.folders;

/*******************************************************************************
    Helper
*******************************************************************************/

var banner = ['/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' * @version v<%= pkg.version %>',
  ' */',
  ''
].join('\n');

var handleErrors = function() {
  var args = Array.prototype.slice.call(arguments);

  // Send error to notification center with gulp-notify
  $.notify.onError({
    title: 'Compile Error',
    message: '<%= error.message %>'
  }).apply(this, args);

  // Keep gulp from hanging on this task
  this.emit('end');
};

/*******************************************************************************
    FILE / PATH / SHIM  CONFIG
*******************************************************************************/

/*******************************************************************************
    CLEAN DEST TASK
*******************************************************************************/
gulp.task('clean', function() {
  return gulp.src(folders.dest, {
      read: false
    })
    .pipe($.rimraf());
  /*return gulp.src([
      path.join(folders.dest, folders.assets.css),
      path.join(folders.dest, folders.assets.js),
      path.join(folders.dest, folders.assets.fonts),
      path.join(folders.dest, folders.assets.images)
    ], {
      read: false
    })
    .pipe($.rimraf());*/
});

/*******************************************************************************
    CSS TASK
*******************************************************************************/

gulp.task('scss', function() {
  return gulp.src(folders.src + '/' + folders.assets.scss + '/' + config.styles.scss.main)
    .pipe($.plumber())
    .pipe($.sass({
      includePaths: folders.components,
      outputStyle: 'expanded'
    }))
    .on('error', handleErrors)
    .pipe(combineMediaQueries({
      log: true
    }))
    .pipe($.autoprefixer(config.autoprefixer.def))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.css))
    .pipe(reload({
      stream: true
    }));
});

gulp.task('write:bp', function() {
  return gulp.src(folders.src + '/' + folders.assets.js + '/app/common.js')
    .pipe($.tap(function(file) {

      var contents = eval(file.contents.toString());

      var str = '// DO NOT EDIT THIS FILE DIRECTLY. IT IS CREATED FROM app/common.js\n\n';
      var bpNames = '$bp-names:';
      for (var i in contents.breakPoints) {
        str += '$' + i + ' : ' + contents.breakPoints[i] + 'px;\n';
        bpNames += ' ' + '\'' + i.replace('bp-', '') + '\'';
      }
      bpNames += ';\n';
      //str+= bpNames;
      file.contents = new Buffer(str);
    }))
    .pipe($.rename('breakpoints.scss'))
    .pipe(gulp.dest(folders.src + '/' + folders.assets.scss + '/app/core/'));
});

// for rules see
// https://github.com/CSSLint/csslint/wiki/Rules-by-ID
gulp.task('csslint', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.css + '/**/*.css')
    .pipe($.csslint())
    .pipe($.csslint.reporter());
});

gulp.task('scsslint', function() {
  gulp.src([folders.src + '/' + folders.assets.scss + '/**/*.scss', '!' + folders.src + '/' + folders.assets.scss + '/vendor/**'])
    .pipe(scsslint({
      'config': folders.src + '/' + folders.assets.scss + '/.scss-lint.yml',
    }));
});

gulp.task('stylestats', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.css + '/main.css')
    .pipe($.stylestats());
});

gulp.task('styles', ['scsslint', 'scss', 'csslint']);

/*******************************************************************************
    JAVASCRIPT TASK
*******************************************************************************/

gulp.task('jshint', function() {
  return gulp.src([folders.src + '/' + folders.assets.js + '/**/*.js', '!' + folders.src + '/' + folders.assets.js + '/vendor/**', '!' + folders.src + '/' + folders.assets.js + '/app/libs/**'])
    .pipe($.jshint('.jshintrc'))
    .pipe($.jshint.reporter(stylish));
});

gulp.task('js', function() {
  return gulp.src(folders.src + '/' + folders.assets.js + '/' + config.js.main)
    .pipe($.plumber())
    .pipe($.browserify({
      insertGlobals: false,
      debug: true,
      // transform: ['jstify', 'debowerify', 'decomponentify', 'deamdify', 'deglobalify'],
      // transform: ['jstify'],
      shim: config.js.shim
    }))
    .on('error', handleErrors)
    .pipe($.rename(config.js.out))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.js))
    .pipe(reload({
      stream: true,
      once: true
    }));
});

gulp.task('scripts', function(cb) {
  return runSequence('jshint', 'js', cb);
});

/*******************************************************************************
    IMAGES / SPRITE TASK
*******************************************************************************/

gulp.task('images', function() {
  return gulp.src([folders.src + '/' + folders.assets.images + '/**/*', '!' + folders.src + '/' + folders.assets.images + '/_**/*', '!' + folders.src + '/' + folders.assets.images + '/_*'])
    .pipe($.cache($.imagemin({
      optimizationLevel: 3,
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.images))

  .on('error', handleErrors);
});

gulp.task('sprite@2x', function() {
  var spriteData = gulp.src(folders.src + '/' + folders.assets.sprites + '/*@2x.png')
    .pipe(spritesmith({
      imgName: 'sprite@2x.png',
      imgPath: '../images/sprite@2x.png',
      padding: 80,
      cssName: 'sprites-2x.scss'
    }));

  return spriteData.img.pipe(gulp.dest(folders.tmp + '/' + folders.assets.images));
});

gulp.task('sprite', function() {
  var spriteData = gulp.src([folders.src + '/' + folders.assets.sprites + '/*.png', '!' + folders.src + '/' + folders.assets.sprites + '/*@2x.png'])
    .pipe(spritesmith({
      imgName: 'sprite.png',
      imgPath: '../images/sprite.png',
      cssName: 'sprites.scss',
      padding: 40,
      cssTemplate: './lib/spritesmith.scss.tpl.mustache',
      cssVarMap: function(sprite) {
        sprite.image = sprite.image.replace('.png', '');
      },
    }));

  spriteData.img.pipe(gulp.dest(folders.tmp + '/' + folders.assets.images));
  return spriteData.css.pipe(gulp.dest(folders.src + '/' + folders.assets.scss + '/app/utils/'));

});

gulp.task('sprites', function(cb) {
  return runSequence(['sprite@2x', 'sprite'], cb);
});

/*******************************************************************************
    TEMPLATE TASK
*******************************************************************************/

/*******************************************************************************
    SERVER TASK
*******************************************************************************/

/*******************************************************************************
    SEMVER TASK
*******************************************************************************/

gulp.task('bump:patch', function() {
  return gulp.src('./package.json')
    .pipe($.bump({
      type: 'patch'
    }))
    .pipe($.tap(function(file) {
      pkg = JSON.parse(file.contents.toString());
    }))
    .pipe(gulp.dest('./'));

});

gulp.task('bump:minor', function() {
  return gulp.src('./package.json')
    .pipe($.bump({
      type: 'minor'
    }))
    .pipe($.tap(function(file) {
      pkg = JSON.parse(file.contents.toString());
    }))
    .pipe(gulp.dest('./'));

});

gulp.task('bump:major', function() {
  return gulp.src('./package.json')
    .pipe($.bump({
      type: 'major'
    }))
    .pipe($.tap(function(file) {
      pkg = JSON.parse(file.contents.toString());
    }))
    .pipe(gulp.dest('./'));
});

/*******************************************************************************
    SETUP TASK
*******************************************************************************/

/*******************************************************************************
    BUILD / DIST TASK
*******************************************************************************/

gulp.task('js:dist', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.js + '/**/*.js')
    .pipe($.uglify())
    .pipe($.ignore.exclude(folders.tmp + '/' + folders.assets.js + '/vendor/**'))
    .pipe($.header(banner, {
      pkg: pkg
    }))
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.js));
});

gulp.task('vendor:dist', function() {
  return gulp.src(folders.src + '/' + folders.assets.js + '/vendor/*.js')
    .pipe($.uglify())
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.js + '/vendor'));
});

gulp.task('css:dist', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.css + '/**/*.css')
    .pipe($.minifyCss({
      // noRebase: true,
      noAdvanced: true,
      keepSpecialComments: 0,
      // compatibility: 'ie8'
    }))
    .pipe($.header(banner, {
      pkg: pkg
    }))
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.css));
});

gulp.task('fonts:dist', function() {
  return gulp.src(folders.src + '/' + folders.assets.fonts + '/**/*')
    .pipe(gulp.dest(folders.dest + '/assets/fonts'));
});

// copy all content from images/* excapt prefixed with _
gulp.task('images:dist', function() {
  return gulp.src([folders.src + '/' + folders.assets.images + '/**/*', '!' + folders.src + '/' + folders.assets.images + '/_**/*', '!' + folders.src + '/' + folders.assets.images + '/_*', folders.tmp + '/' + folders.assets.images + '/**/*', folders.tmp + '/' + folders.assets.images + '/**/*'])
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.images));
});

gulp.task('html:tmp:dist', function() {
  return gulp.src(folders.tmp + '/**/*.{html,shtml,php,xml,json,webapp,txt,ico}')
    .pipe(gulp.dest(folders.dest));
});

gulp.task('html:src:dist', function() {
  return gulp.src([folders.src + '/*.*', folders.src + '/.*', folders.src + '/**/*.{html,shtml,php,xml,json}', '!' + folders.components + '/**/*', '!' + folders.src + '/' + folders.assets.js + '/**/*'])
    .pipe(gulp.dest(folders.dest));
});

gulp.task('dist', function(cb) {
  runSequence('clean', 'default', ['js:dist', 'vendor:dist', 'css:dist', 'fonts:dist', 'images:dist', 'pkg:dist'], 'html:src:dist', 'html:tmp:dist', 'htaccess:dist', cb);
});

gulp.task('dist:bump', ['bump:patch', 'dist']);

/*******************************************************************************
    DEPLOY TASK
*******************************************************************************/

gulp.task('deploy', ['dist'], function(cb) {
  rsync({
    args: ['--verbose'],
    src: './' + folders.dest + '/',
    exclude: ['.git', '.DS_Store', '.gitattributes', '.gitignore'],
    ssh: true,
    dest: config.deploy.ssh.dest,
    recursive: true,
    syncDestIgnoreExcl: true,
    dryRun: false
  }, function(err, stdout, stderr, cmd) {
    console.log('Shell command was: ' + cmd);

    if (err) {
      cb(err);
    } else {
      console.log(stdout);
      cb();
    }
  });
});

/*******************************************************************************
    PageSpeed TASK
*******************************************************************************/

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'https://example.com',
  strategy: 'mobile'
}));

/*******************************************************************************
    WATCH / SERVE TASK
*******************************************************************************/

// Build and serve the output from the dist build
gulp.task('serve:dist', ['dist'], function() {
  var openBrowserWindow = !($.util.env.restart || false);

  browserSync({
    notify: false,
    open: openBrowserWindow,
    ghostMode: false,
    server: {
      baseDir: folders.dest
    }
  });
});

gulp.task('watch', function() {

  var openBrowserWindow = !($.util.env.restart || false);
  var ghostMode = !($.util.env.ghostMode || false);

  browserSync({
    notify: false,
    open: openBrowserWindow,
    ghostMode: ghostMode,

    server: {
      baseDir: [folders.tmp, folders.src]
    }
  });

  // Watch for changes in `src` folder
  gulp.watch(folders.src + '/**/*.{html,shtml,php,xml,json}', reload);

  // Watch .scss files
  gulp.watch(folders.src + '/' + folders.assets.scss + '/**/*.scss', ['scss']);

  // Watch .stylus files
  gulp.watch(folders.src + '/' + folders.assets.stylus + '/**/*.styl', ['stylus']);

  // Watch .js files
  gulp.watch([folders.src + '/' + folders.assets.js + '/**/*.js'], ['js']);

  // Watch .jade files
  gulp.watch(folders.src + '/' + folders.jade + '/**/*.jade', ['jade']);

  // Watch sprite changes
  gulp.watch(folders.src + '/' + folders.assets.sprites + '/*.png', ['sprites', reload]);

  // Watch image files
  // gulp.watch(folders.src + '/assets/images/**/*', ['images']);
  //
  // Watch sprite changes
  gulp.watch(folders.src + '/' + folders.assets.sprites + '/*.png', ['sprites']);

});

gulp.task('default', function(cb) {
  return runSequence('sprites', ['scripts', 'styles'], cb);
});

gulp.task('dev', function(cb) {
  return runSequence('default', 'watch', cb);
});
<commit_msg>reoptimize gulpfile<commit_after>'use strict';

/*******************************************************************************
    DEPENDENCIES
*******************************************************************************/

var gulp = require('gulp');
var $ = require('gulp-load-plugins')();

var browserSync = require('browser-sync');
var reload = browserSync.reload;
var scsslint = require('gulp-scss-lint');

var runSequence = require('run-sequence');
var pagespeed = require('psi');
var fs = require('fs');
var rsync = require('rsyncwrapper').rsync;
var pkg = require('./package.json');

var spritesmith = require('gulp.spritesmith');
var combineMediaQueries = require('gulp-combine-media-queries');

var stylish = require('jshint-stylish');

var path = require('path');
var config = require('./config');
var folders = config.folders;

/*******************************************************************************
    Helper
*******************************************************************************/

var banner = ['/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' * @version v<%= pkg.version %>',
  ' */',
  ''
].join('\n');

var handleErrors = function() {
  var args = Array.prototype.slice.call(arguments);

  // Send error to notification center with gulp-notify
  $.notify.onError({
    title: 'Compile Error',
    message: '<%= error.message %>'
  }).apply(this, args);

  // Keep gulp from hanging on this task
  this.emit('end');
};

/*******************************************************************************
    FILE / PATH / SHIM  CONFIG
*******************************************************************************/

/*******************************************************************************
    CLEAN DEST TASK
*******************************************************************************/
gulp.task('clean', function() {
  return gulp.src(folders.dest, {
      read: false
    })
    .pipe($.rimraf());
  /*return gulp.src([
      path.join(folders.dest, folders.assets.css),
      path.join(folders.dest, folders.assets.js),
      path.join(folders.dest, folders.assets.fonts),
      path.join(folders.dest, folders.assets.images)
    ], {
      read: false
    })
    .pipe($.rimraf());*/
});

/*******************************************************************************
    CSS TASK
*******************************************************************************/

gulp.task('scss', function() {
  return gulp.src(folders.src + '/' + folders.assets.scss + '/' + config.styles.scss.main)
    .pipe($.plumber())
    .pipe($.sass({
      includePaths: folders.components,
      outputStyle: 'expanded'
    }))
    .on('error', handleErrors)
    .pipe(combineMediaQueries({
      log: true
    }))
    .pipe($.autoprefixer(config.autoprefixer.def))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.css))
    .pipe(reload({
      stream: true
    }));
});

gulp.task('compass', function() {
  return gulp.src(folders.src + '/' + folders.assets.scss + '/' + config.styles.scss.main)
    .pipe($.plumber())
    .pipe($.compass({
      css: folders.tmp + '/' + folders.assets.css,
      sass: folders.src + '/' + folders.assets.scss,
      image: folders.src + '/' + folders.assets.images,
      javascripts: folders.src + '/' + folders.assets.js,
      fonts: folders.src + '/' + folders.assets.fonts,
      import_path: folders.components
      //, require: ['susy', 'modular-scale']
    }))
    .on('error', handleErrors)
    .pipe($.autoprefixer(config.autoprefixer.def))
    .pipe(gulp.dest(folders.tmp + '/css'))
    .pipe(reload({
      stream: true
    }));
});

gulp.task('stylus', function() {
  return gulp.src(folders.src + '/' + folders.assets.stylus + '/' + config.styles.stylus.main)
    .pipe($.plumber())
    .pipe($.stylus({
      //use: nib(),
      url: {
        name: 'embedurl',
        paths: [__dirname + '/' + folders.src + '/' + folders.assets.images],
        limit: false
      },
      error: true
    }))
    .on('error', handleErrors)
    .pipe($.autoprefixer.apply(config.autoprefixer.def))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.css))
    .pipe(reload({
      stream: true
    }));
});

// for rules see
// https://github.com/CSSLint/csslint/wiki/Rules-by-ID
gulp.task('csslint', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.css + '/**/*.css')
    .pipe($.csslint())
    .pipe($.csslint.reporter());
});

gulp.task('scsslint', function() {
  gulp.src([folders.src + '/' + folders.assets.scss + '/**/*.scss', '!' + folders.src + '/' + folders.assets.scss + '/vendor/**'])
    .pipe(scsslint({
      'config': folders.src + '/' + folders.assets.scss + '/.scss-lint.yml',
    }));
});

gulp.task('stylestats', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.css + '/main.css')
    .pipe($.stylestats());
});

//TODO gulp-if
gulp.task('styles', ['scsslint', 'scss', 'csslint']);
gulp.task('styles', ['stylus', 'csslint']);

/*******************************************************************************
    JAVASCRIPT TASK
*******************************************************************************/

gulp.task('jshint', function() {
  return gulp.src([folders.src + '/' + folders.assets.js + '/**/*.js', '!' + folders.src + '/' + folders.assets.js + '/vendor/**', '!' + folders.src + '/' + folders.assets.js + '/app/libs/**'])
    .pipe($.jshint('.jshintrc'))
    .pipe($.jshint.reporter(stylish));
});

gulp.task('js', function() {
  return gulp.src(folders.src + '/' + folders.assets.js + '/' + config.js.main)
    .pipe($.plumber())
    .pipe($.browserify({
      insertGlobals: false,
      debug: true,
      shim: config.js.shim
      // transform: ['jstify', 'debowerify', 'decomponentify', 'deamdify', 'deglobalify'],
      // transform: ['jstify'],
    }))
    .on('error', handleErrors)
    .pipe($.rename(config.js.out))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.js))
    .pipe(reload({
      stream: true,
      once: true
    }));
});

gulp.task('scripts', function(cb) {
  return runSequence('jshint', 'js', cb);
});

/*******************************************************************************
    IMAGES / SPRITE TASK
*******************************************************************************/

gulp.task('images', function() {
  return gulp.src([folders.src + '/' + folders.assets.images + '/**/*', '!' + folders.src + '/' + folders.assets.images + '/_**/*', '!' + folders.src + '/' + folders.assets.images + '/_*'])
    .pipe($.cache($.imagemin({
      optimizationLevel: 3,
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest(folders.tmp + '/' + folders.assets.images))

  .on('error', handleErrors);
});

gulp.task('sprite@2x', function() {
  var spriteData = gulp.src(folders.src + '/' + folders.assets.sprites + '/*@2x.png')
    .pipe(spritesmith({
      imgName: 'sprite@2x.png',
      imgPath: '../images/sprite@2x.png',
      padding: 80,
      cssName: 'sprites-2x.scss'
    }));

  return spriteData.img.pipe(gulp.dest(folders.tmp + '/' + folders.assets.images));
});

gulp.task('sprite', function() {
  var spriteData = gulp.src([folders.src + '/' + folders.assets.sprites + '/*.png', '!' + folders.src + '/' + folders.assets.sprites + '/*@2x.png'])
    .pipe(spritesmith({
      imgName: 'sprite.png',
      imgPath: '../images/sprite.png',
      cssName: 'sprites.scss',
      padding: 40,
      cssTemplate: './lib/spritesmith.scss.tpl.mustache',
      cssVarMap: function(sprite) {
        sprite.image = sprite.image.replace('.png', '');
      },
    }));

  spriteData.img.pipe(gulp.dest(folders.tmp + '/' + folders.assets.images));
  return spriteData.css.pipe(gulp.dest(folders.src + '/' + folders.assets.scss + '/app/utils/'));

});

gulp.task('sprites', function(cb) {
  return runSequence(['sprite@2x', 'sprite'], cb);
});

/*******************************************************************************
    TEMPLATE TASK
*******************************************************************************/

/*******************************************************************************
    SERVER TASK
*******************************************************************************/

/*******************************************************************************
    SEMVER TASK
*******************************************************************************/

gulp.task('bump:patch', function() {
  return gulp.src('./package.json')
    .pipe($.bump({
      type: 'patch'
    }))
    .pipe($.tap(function(file) {
      pkg = JSON.parse(file.contents.toString());
    }))
    .pipe(gulp.dest('./'));

});

gulp.task('bump:minor', function() {
  return gulp.src('./package.json')
    .pipe($.bump({
      type: 'minor'
    }))
    .pipe($.tap(function(file) {
      pkg = JSON.parse(file.contents.toString());
    }))
    .pipe(gulp.dest('./'));

});

gulp.task('bump:major', function() {
  return gulp.src('./package.json')
    .pipe($.bump({
      type: 'major'
    }))
    .pipe($.tap(function(file) {
      pkg = JSON.parse(file.contents.toString());
    }))
    .pipe(gulp.dest('./'));
});

/*******************************************************************************
    SETUP TASK
*******************************************************************************/

/*******************************************************************************
    BUILD / DIST TASK
*******************************************************************************/

gulp.task('js:dist', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.js + '/**/*.js')
    .pipe($.uglify())
    .pipe($.ignore.exclude(folders.tmp + '/' + folders.assets.js + '/vendor/**'))
    .pipe($.header(banner, {
      pkg: pkg
    }))
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.js));
});

gulp.task('vendor:dist', function() {
  return gulp.src(folders.src + '/' + folders.assets.js + '/vendor/*.js')
    .pipe($.uglify())
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.js + '/vendor'));
});

gulp.task('css:dist', function() {
  return gulp.src(folders.tmp + '/' + folders.assets.css + '/**/*.css')
    .pipe($.minifyCss({
      // noRebase: true,
      noAdvanced: true,
      keepSpecialComments: 0,
      // compatibility: 'ie8'
    }))
    .pipe($.header(banner, {
      pkg: pkg
    }))
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.css));
});

gulp.task('fonts:dist', function() {
  return gulp.src(folders.src + '/' + folders.assets.fonts + '/**/*')
    .pipe(gulp.dest(folders.dest + '/assets/fonts'));
});

// copy all content from images/* excapt prefixed with _
gulp.task('images:dist', function() {
  return gulp.src([folders.src + '/' + folders.assets.images + '/**/*', '!' + folders.src + '/' + folders.assets.images + '/_**/*', '!' + folders.src + '/' + folders.assets.images + '/_*', folders.tmp + '/' + folders.assets.images + '/**/*', folders.tmp + '/' + folders.assets.images + '/**/*'])
    .pipe(gulp.dest(folders.dest + '/' + folders.assets.images));
});

gulp.task('html:tmp:dist', function() {
  return gulp.src(folders.tmp + '/**/*.{html,shtml,php,xml,json,webapp,txt,ico}')
    .pipe(gulp.dest(folders.dest));
});

gulp.task('html:src:dist', function() {
  return gulp.src([folders.src + '/*.*', folders.src + '/.*', folders.src + '/**/*.{html,shtml,php,xml,json}', '!' + folders.components + '/**/*', '!' + folders.src + '/' + folders.assets.js + '/**/*'])
    .pipe(gulp.dest(folders.dest));
});

gulp.task('dist', function(cb) {
  runSequence('clean', 'default', ['js:dist', 'vendor:dist', 'css:dist', 'fonts:dist', 'images:dist', 'pkg:dist'], 'html:src:dist', 'html:tmp:dist', 'htaccess:dist', cb);
});

gulp.task('dist:bump', ['bump:patch', 'dist']);

/*******************************************************************************
    DEPLOY TASK
*******************************************************************************/

gulp.task('deploy', ['dist'], function(cb) {
  rsync({
    args: ['--verbose'],
    src: './' + folders.dest + '/',
    exclude: ['.git', '.DS_Store', '.gitattributes', '.gitignore'],
    ssh: true,
    dest: config.deploy.ssh.dest,
    recursive: true,
    syncDestIgnoreExcl: true,
    dryRun: false
  }, function(err, stdout, stderr, cmd) {
    console.log('Shell command was: ' + cmd);

    if (err) {
      cb(err);
    } else {
      console.log(stdout);
      cb();
    }
  });
});

/*******************************************************************************
    PageSpeed TASK
*******************************************************************************/

// Run PageSpeed Insights
// Update `url` below to the public URL for your site
gulp.task('pagespeed', pagespeed.bind(null, {
  // By default, we use the PageSpeed Insights
  // free (no API key) tier. You can use a Google
  // Developer API key if you have one. See
  // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY'
  url: 'https://example.com',
  strategy: 'mobile'
}));

/*******************************************************************************
    WATCH / SERVE TASK
*******************************************************************************/

// Build and serve the output from the dist build
gulp.task('serve:dist', ['dist'], function() {
  var openBrowserWindow = !($.util.env.restart || false);

  browserSync({
    notify: false,
    open: openBrowserWindow,
    ghostMode: false,
    server: {
      baseDir: folders.dest
    }
  });
});

gulp.task('watch', function() {

  var openBrowserWindow = !($.util.env.restart || false);
  var ghostMode = !($.util.env.ghostMode || false);

  browserSync({
    notify: false,
    open: openBrowserWindow,
    ghostMode: ghostMode,

    server: {
      baseDir: [folders.tmp, folders.src]
    }
  });

  // Watch for changes in `src` folder
  gulp.watch(folders.src + '/**/*.{html,shtml,php,xml,json}', reload);

  // Watch .scss files
  gulp.watch(folders.src + '/' + folders.assets.scss + '/**/*.scss', ['scss']);

  // Watch .stylus files
  gulp.watch(folders.src + '/' + folders.assets.stylus + '/**/*.styl', ['stylus']);

  // Watch .js files
  gulp.watch([folders.src + '/' + folders.assets.js + '/**/*.js'], ['js']);

  // Watch .jade files
  gulp.watch(folders.src + '/' + folders.jade + '/**/*.jade', ['jade']);

  // Watch sprite changes
  gulp.watch(folders.src + '/' + folders.assets.sprites + '/*.png', ['sprites', reload]);

  // Watch image files
  // gulp.watch(folders.src + '/assets/images/**/*', ['images']);
  //
  // Watch sprite changes
  gulp.watch(folders.src + '/' + folders.assets.sprites + '/*.png', ['sprites']);

});

gulp.task('default', function(cb) {
  return runSequence('sprites', ['scripts', 'styles'], cb);
});

gulp.task('dev', function(cb) {
  return runSequence('default', 'watch', cb);
});
<|endoftext|>


<commit_before>var express = require('express');
var app = require('./app');
var http = require('http');
var debug = require('debug');

app.testing = true;

var server = http.createServer(app);
var port = normalizePort(process.env.PORT || '5001');
server.listen(port);

server.on('error', onError);
server.on('listening', onListening);

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}
<commit_msg>Removed function for port normalization<commit_after>var express = require('express');
var app = require('./app');
var http = require('http');
var debug = require('debug');

app.testing = true;

var server = http.createServer(app);
var port = process.env.PORT || '5001';
server.listen(port);

server.on('error', onError);
server.on('listening', onListening);

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}
<|endoftext|>


<commit_before>/*
 * ***** BEGIN LICENSE BLOCK *****
 * Copyright (c) 2011-2012 VMware, Inc.
 *
 * For the license see COPYING.
 * ***** END LICENSE BLOCK *****
 */

var SockJS = function(url, dep_protocols_whitelist, options) {
    var that = this, protocols_whitelist;
    that._options = {devel: false, debug: false, protocols_whitelist: [],
                     info: undefined, rtt: undefined};
    if (options) {
        utils.objectExtend(that._options, options);
    }
    that._base_url = utils.amendUrl(url);
    that._server = that._options.server || utils.random_number_string(1000);
    if (that._options.protocols_whitelist &&
        that._options.protocols_whitelist.length) {
        protocols_whitelist = that._options.protocols_whitelist;
    } else {
        // Deprecated API
        if (typeof dep_protocols_whitelist === 'string' &&
            dep_protocols_whitelist.length > 0) {
            protocols_whitelist = [dep_protocols_whitelist];
        } else if (utils.isArray(dep_protocols_whitelist)) {
            protocols_whitelist = dep_protocols_whitelist
        } else {
            protocols_whitelist = null;
        }
        if (protocols_whitelist) {
            that._debug('Deprecated API: Use "protocols_whitelist" option ' +
                        'instead of supplying protocol list as a second ' +
                        'parameter to SockJS constructor.');
        }
    }
    that._protocols = [];
    that.protocol = null;
    that.readyState = SockJS.CONNECTING;
    var ir = createInfoReceiver(that._base_url);
    ir.onfinish = function(info, rtt) {
        if (info) {
            if (that._options.info) {
                // Override if user supplies the option
                info = utils.objectExtend(info, that._options.info);
            }
            if (that._options.rtt) {
                rtt = that._options.rtt;
            }
            that._applyInfo(info, rtt, protocols_whitelist);
            that._didClose();
        } else {
            that._didClose(1002, 'Can\'t connect to server', true);
        }
    };
};
// Inheritance
SockJS.prototype = new REventTarget();

SockJS.version = "<!-- version -->";

SockJS.CONNECTING = 0;
SockJS.OPEN = 1;
SockJS.CLOSING = 2;
SockJS.CLOSED = 3;

SockJS.prototype._debug = function() {
    if (this._options.debug)
        utils.log.apply(utils, arguments);
};

SockJS.prototype._dispatchOpen = function() {
    var that = this;
    if (that.readyState === SockJS.CONNECTING) {
        if (that._transport_tref) {
            clearTimeout(that._transport_tref);
            that._transport_tref = null;
        }
        that.readyState = SockJS.OPEN;
        that.dispatchEvent(new SimpleEvent("open"));
    } else {
        // The server might have been restarted, and lost track of our
        // connection.
        that._didClose(1006, "Server lost session");
    }
};

SockJS.prototype._dispatchMessage = function(data) {
    var that = this;
    if (that.readyState !== SockJS.OPEN)
            return;
    that.dispatchEvent(new SimpleEvent("message", {data: data}));
};

SockJS.prototype._dispatchHeartbeat = function(data) {
    var that = this;
    if (that.readyState !== SockJS.OPEN)
        return;
    that.dispatchEvent(new SimpleEvent('heartbeat', {}));
};

SockJS.prototype._didClose = function(code, reason, force) {
    var that = this;
    if (that.readyState !== SockJS.CONNECTING &&
        that.readyState !== SockJS.OPEN &&
        that.readyState !== SockJS.CLOSING)
            throw new Error('INVALID_STATE_ERR');
    if (that._transport)
        that._transport.doCleanup();
    that._transport = null;

    var close_event = new SimpleEvent("close", {
        code: code,
        reason: reason,
        wasClean: utils.userSetCode(code)});

    if (!utils.userSetCode(code) &&
        that.readyState === SockJS.CONNECTING && !force) {
        if (that._try_next_protocol(close_event)) {
            return;
        }
        close_event = new SimpleEvent("close", {code: 2000,
                                                reason: "All transports failed",
                                                wasClean: false,
                                                last_event: close_event});
    }
    that.readyState = SockJS.CLOSED;

    utils.delay(function() {
                   that.dispatchEvent(close_event);
                });
};

SockJS.prototype._didMessage = function(data) {
    var that = this;
    var type = data.slice(0, 1);
    switch(type) {
    case 'o':
        that._dispatchOpen();
        break;
    case 'a':
        var payload = JSON.parse(data.slice(1) || '[]');
        for(var i=0; i < payload.length; i++){
            that._dispatchMessage(payload[i]);
        }
        break;
    case 'm':
        var payload = JSON.parse(data.slice(1) || 'null');
        that._dispatchMessage(payload);
        break;
    case 'c':
        var payload = JSON.parse(data.slice(1) || '[]');
        that._didClose(payload[0], payload[1]);
        break;
    case 'h':
        that._dispatchHeartbeat();
        break;
    }
};

SockJS.prototype._try_next_protocol = function(close_event) {
    var that = this;
    if (that.protocol) {
        that._debug('Closed transport:', that.protocol, ''+close_event);
        that.protocol = null;
    }
    if (that._transport_tref) {
        clearTimeout(that._transport_tref);
        that._transport_tref = null;
    }

    while(1) {
        var protocol = that.protocol = that._protocols.shift();
        if (!protocol) {
            return false;
        }
        // Some protocols require access to `body`, what if were in
        // the `head`?
        if (SockJS[protocol] &&
            SockJS[protocol].need_body === true &&
            (!_document.body ||
             (typeof _document.readyState !== 'undefined'
              && _document.readyState !== 'complete'))) {
            that._protocols.unshift(protocol);
            that.protocol = 'waiting-for-load';
            utils.attachEvent('load', function(){
                that._try_next_protocol();
            });
            return true;
        }

        if (!SockJS[protocol] ||
              !SockJS[protocol].enabled(that._options)) {
            that._debug('Skipping transport:', protocol);
        } else {
            var roundTrips = SockJS[protocol].roundTrips || 1;
            var to = ((that._options.rto || 0) * roundTrips) || 5000;
            that._transport_tref = utils.delay(to, function() {
                if (that.readyState === SockJS.CONNECTING) {
                    // I can't understand how it is possible to run
                    // this timer, when the state is CLOSED, but
                    // apparently in IE everythin is possible.
                    that._didClose(2007, "Transport timeouted");
                }
            });

            var connid = utils.random_string(8);
            var trans_url = that._base_url + '/' + that._server + '/' + connid;
            that._debug('Opening transport:', protocol, ' url:'+trans_url,
                        ' RTO:'+that._options.rto);
            that._transport = new SockJS[protocol](that, trans_url,
                                                   that._base_url);
            return true;
        }
    }
};

SockJS.prototype.close = function(code, reason) {
    var that = this;
    if (code && !utils.userSetCode(code))
        throw new Error("INVALID_ACCESS_ERR");
    if(that.readyState !== SockJS.CONNECTING &&
       that.readyState !== SockJS.OPEN) {
        return false;
    }
    that.readyState = SockJS.CLOSING;
    that._didClose(code || 1000, reason || "Normal closure");
    return true;
};

SockJS.prototype.send = function(data) {
    var that = this;
    if (that.readyState === SockJS.CONNECTING)
        throw new Error('INVALID_STATE_ERR');
    if (that.readyState === SockJS.OPEN) {
        that._transport.doSend(utils.quote('' + data));
    }
    return true;
};

SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) {
    var that = this;
    that._options.info = info;
    that._options.rtt = rtt;
    that._options.rto = utils.countRTO(rtt);
    that._options.info.null_origin = !_document.domain;
    var probed = utils.probeProtocols();
    that._protocols = utils.detectProtocols(probed, protocols_whitelist, info);
};
<commit_msg>Canceling SockJS before "info" exited caused an exception in IE.<commit_after>/*
 * ***** BEGIN LICENSE BLOCK *****
 * Copyright (c) 2011-2012 VMware, Inc.
 *
 * For the license see COPYING.
 * ***** END LICENSE BLOCK *****
 */

var SockJS = function(url, dep_protocols_whitelist, options) {
    var that = this, protocols_whitelist;
    that._options = {devel: false, debug: false, protocols_whitelist: [],
                     info: undefined, rtt: undefined};
    if (options) {
        utils.objectExtend(that._options, options);
    }
    that._base_url = utils.amendUrl(url);
    that._server = that._options.server || utils.random_number_string(1000);
    if (that._options.protocols_whitelist &&
        that._options.protocols_whitelist.length) {
        protocols_whitelist = that._options.protocols_whitelist;
    } else {
        // Deprecated API
        if (typeof dep_protocols_whitelist === 'string' &&
            dep_protocols_whitelist.length > 0) {
            protocols_whitelist = [dep_protocols_whitelist];
        } else if (utils.isArray(dep_protocols_whitelist)) {
            protocols_whitelist = dep_protocols_whitelist
        } else {
            protocols_whitelist = null;
        }
        if (protocols_whitelist) {
            that._debug('Deprecated API: Use "protocols_whitelist" option ' +
                        'instead of supplying protocol list as a second ' +
                        'parameter to SockJS constructor.');
        }
    }
    that._protocols = [];
    that.protocol = null;
    that.readyState = SockJS.CONNECTING;
    that._ir = createInfoReceiver(that._base_url);
    that._ir.onfinish = function(info, rtt) {
        that._ir = null;
        if (info) {
            if (that._options.info) {
                // Override if user supplies the option
                info = utils.objectExtend(info, that._options.info);
            }
            if (that._options.rtt) {
                rtt = that._options.rtt;
            }
            that._applyInfo(info, rtt, protocols_whitelist);
            that._didClose();
        } else {
            that._didClose(1002, 'Can\'t connect to server', true);
        }
    };
};
// Inheritance
SockJS.prototype = new REventTarget();

SockJS.version = "<!-- version -->";

SockJS.CONNECTING = 0;
SockJS.OPEN = 1;
SockJS.CLOSING = 2;
SockJS.CLOSED = 3;

SockJS.prototype._debug = function() {
    if (this._options.debug)
        utils.log.apply(utils, arguments);
};

SockJS.prototype._dispatchOpen = function() {
    var that = this;
    if (that.readyState === SockJS.CONNECTING) {
        if (that._transport_tref) {
            clearTimeout(that._transport_tref);
            that._transport_tref = null;
        }
        that.readyState = SockJS.OPEN;
        that.dispatchEvent(new SimpleEvent("open"));
    } else {
        // The server might have been restarted, and lost track of our
        // connection.
        that._didClose(1006, "Server lost session");
    }
};

SockJS.prototype._dispatchMessage = function(data) {
    var that = this;
    if (that.readyState !== SockJS.OPEN)
            return;
    that.dispatchEvent(new SimpleEvent("message", {data: data}));
};

SockJS.prototype._dispatchHeartbeat = function(data) {
    var that = this;
    if (that.readyState !== SockJS.OPEN)
        return;
    that.dispatchEvent(new SimpleEvent('heartbeat', {}));
};

SockJS.prototype._didClose = function(code, reason, force) {
    var that = this;
    if (that.readyState !== SockJS.CONNECTING &&
        that.readyState !== SockJS.OPEN &&
        that.readyState !== SockJS.CLOSING)
            throw new Error('INVALID_STATE_ERR');
    if (that._ir) {
        that._ir.nuke();
        that._ir = null;
    }

    if (that._transport) {
        that._transport.doCleanup();
        that._transport = null;
    }

    var close_event = new SimpleEvent("close", {
        code: code,
        reason: reason,
        wasClean: utils.userSetCode(code)});

    if (!utils.userSetCode(code) &&
        that.readyState === SockJS.CONNECTING && !force) {
        if (that._try_next_protocol(close_event)) {
            return;
        }
        close_event = new SimpleEvent("close", {code: 2000,
                                                reason: "All transports failed",
                                                wasClean: false,
                                                last_event: close_event});
    }
    that.readyState = SockJS.CLOSED;

    utils.delay(function() {
                   that.dispatchEvent(close_event);
                });
};

SockJS.prototype._didMessage = function(data) {
    var that = this;
    var type = data.slice(0, 1);
    switch(type) {
    case 'o':
        that._dispatchOpen();
        break;
    case 'a':
        var payload = JSON.parse(data.slice(1) || '[]');
        for(var i=0; i < payload.length; i++){
            that._dispatchMessage(payload[i]);
        }
        break;
    case 'm':
        var payload = JSON.parse(data.slice(1) || 'null');
        that._dispatchMessage(payload);
        break;
    case 'c':
        var payload = JSON.parse(data.slice(1) || '[]');
        that._didClose(payload[0], payload[1]);
        break;
    case 'h':
        that._dispatchHeartbeat();
        break;
    }
};

SockJS.prototype._try_next_protocol = function(close_event) {
    var that = this;
    if (that.protocol) {
        that._debug('Closed transport:', that.protocol, ''+close_event);
        that.protocol = null;
    }
    if (that._transport_tref) {
        clearTimeout(that._transport_tref);
        that._transport_tref = null;
    }

    while(1) {
        var protocol = that.protocol = that._protocols.shift();
        if (!protocol) {
            return false;
        }
        // Some protocols require access to `body`, what if were in
        // the `head`?
        if (SockJS[protocol] &&
            SockJS[protocol].need_body === true &&
            (!_document.body ||
             (typeof _document.readyState !== 'undefined'
              && _document.readyState !== 'complete'))) {
            that._protocols.unshift(protocol);
            that.protocol = 'waiting-for-load';
            utils.attachEvent('load', function(){
                that._try_next_protocol();
            });
            return true;
        }

        if (!SockJS[protocol] ||
              !SockJS[protocol].enabled(that._options)) {
            that._debug('Skipping transport:', protocol);
        } else {
            var roundTrips = SockJS[protocol].roundTrips || 1;
            var to = ((that._options.rto || 0) * roundTrips) || 5000;
            that._transport_tref = utils.delay(to, function() {
                if (that.readyState === SockJS.CONNECTING) {
                    // I can't understand how it is possible to run
                    // this timer, when the state is CLOSED, but
                    // apparently in IE everythin is possible.
                    that._didClose(2007, "Transport timeouted");
                }
            });

            var connid = utils.random_string(8);
            var trans_url = that._base_url + '/' + that._server + '/' + connid;
            that._debug('Opening transport:', protocol, ' url:'+trans_url,
                        ' RTO:'+that._options.rto);
            that._transport = new SockJS[protocol](that, trans_url,
                                                   that._base_url);
            return true;
        }
    }
};

SockJS.prototype.close = function(code, reason) {
    var that = this;
    if (code && !utils.userSetCode(code))
        throw new Error("INVALID_ACCESS_ERR");
    if(that.readyState !== SockJS.CONNECTING &&
       that.readyState !== SockJS.OPEN) {
        return false;
    }
    that.readyState = SockJS.CLOSING;
    that._didClose(code || 1000, reason || "Normal closure");
    return true;
};

SockJS.prototype.send = function(data) {
    var that = this;
    if (that.readyState === SockJS.CONNECTING)
        throw new Error('INVALID_STATE_ERR');
    if (that.readyState === SockJS.OPEN) {
        that._transport.doSend(utils.quote('' + data));
    }
    return true;
};

SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) {
    var that = this;
    that._options.info = info;
    that._options.rtt = rtt;
    that._options.rto = utils.countRTO(rtt);
    that._options.info.null_origin = !_document.domain;
    var probed = utils.probeProtocols();
    that._protocols = utils.detectProtocols(probed, protocols_whitelist, info);
};
<|endoftext|>