<commit_before>var Board = require("../lib/board.js"),
  Descriptor = require("descriptor"),
  __ = require("../lib/fn.js"),
  events = require("events"),
  util = require("util");

var priv = new WeakMap(),
  modes;

modes = {
  INPUT: 0x00,
  OUTPUT: 0x01,
  ANALOG: 0x02,
  PWM: 0x03,
  SERVO: 0x04
};

/**
 * Pin
 * @constructor
 *
 * @description Direct Pin access objects
 *
 * @param {Object} opts Options: pin, freq, range
 */

function Pin(opts) {
  var isPin, isAnalog, addr, type, highLow, value;

  if (!(this instanceof Pin)) {
    return new Pin(opts);
  }

  isAnalog = Pin.isAnalog(opts);

  // Initialize a Device instance on a Board
  Board.Device.call(
    this, opts = Board.Options(opts)
  );

  opts.addr = opts.addr || opts.pin;

  // Create a private side table
  priv.set(this, {
    mode: this.as || 0x00,
    last: null
  });

  isPin = typeof opts !== "object";
  addr = isPin ? opts : opts.addr;
  type = opts.type || (isAnalog ? "analog" : "digital");
  value = 0;

  // Create read-only "addr(address)" property
  Object.defineProperties(this, {
    type: new Descriptor(type),
    addr: new Descriptor(addr, "!writable"),
    value: {
      get: function() {
        return value;
      }
    }
  });

  this.firmata.pinMode(
    this.addr, priv.get(this).mode
  );

  highLow = function(state) {
    return function(data) {
      var privs, isNot, emit;

      // Update the value closure
      value = data;

      privs = priv.get(this);
      isNot = state ? "low" : "high";
      emit = state ? "high" : "low";

      if (privs.mode === modes.INPUT) {
        if (privs.last === null) {
          privs.last = isNot;
        }
        if (data === state && privs.last === isNot) {
          privs.last = emit;
          this.emit(emit);
        }
      }

      // Emit a firehose "data" event
      this.emit("data");

    }.bind(this);
  }.bind(this);

  // Debounced for noise reduction: more accurately
  // detect HIGH state.
  this.firmata[type + "Read"](
    this.addr, __.debounce(highLow(1), 50)
  );

  // No debounce to read the constant stream
  // (very noisy, only care about 0)
  this.firmata[type + "Read"](
    this.addr, highLow(0)
  );
}

util.inherits(Pin, events.EventEmitter);

/**
 * Pin.@@MODE
 *
 * Read-only constants
 * Pin.INPUT   = 0x00
 * Pin.OUTPUT  = 0x01
 * Pin.ANALOG  = 0x02
 * Pin.PWM     = 0x03
 * Pin.SERVO   = 0x04
 *
 */
Object.keys(modes).forEach(function(mode) {
  Object.defineProperty(Pin, mode, {
    value: modes[mode]
  });
});

Pin.isAnalog = function(opts) {
  if (typeof opts === "string" && Pin.isPrefixed(opts, ["I", "A"])) {
    return true;
  }

  if (typeof opts === "object") {
    return Pin.isAnalog(
      typeof opts.addr !== "undefined" ? opts.addr : opts.pin
    );
  }
};

Pin.isPrefixed = function(value, prefixes) {
  value = value[0];

  return prefixes.reduce(function(resolution, prefix) {
    if (!resolution) {
      return prefix === value;
    }
    return resolution;
  }, false);
};

Pin.write = function(pin, val) {
  // Set the correct mode (OUTPUT)
  // This will only set if it needs to be set, otherwise a no-op
  pin.mode(modes.OUTPUT);

  // Create the correct type of write command
  pin.firmata[pin.type + "Write"](pin.addr, val);
};

Pin.read = function(pin, callback) {
  // Set the correct mode (INPUT)
  // This will only set if it needs to be set, otherwise a no-op
  pin.mode(modes.INPUT);

  pin.firmata[pin.type + "Read"](pin.addr, function() {
    callback.apply(pin, arguments);
  });
};

// Pin.prototype.isDigital = function() {
//   return this.addr > 1;
// };

// Pin.prototype.isAnalog = function() {
//   return this.board > 1;
// };

// Pin.prototype.isPWM = function() {
// };

// Pin.prototype.isServo = function() {
// };

// Pin.prototype.isI2C = function() {
// };

// Pin.prototype.isSerial = function() {
// };

// Pin.prototype.isInterrupt = function() {
// };

// Pin.prototype.isVersion = function() {
// };

Pin.prototype.query = function(callback) {
  function handler() {
    callback(this.firmata.pins[this.addr]);
  }

  this.firmata.queryPinState(this.addr, handler.bind(this));

  return this;
};

/**
 * high  Write high/1 to the pin
 * @return {Pin}
 */

Pin.prototype.high = function() {
  var value = this.type === "analog" ? 255 : 1;
  Pin.write(this, value);
  this.emit("high");
  return this;
};

/**
 * low  Write low/0 to the pin
 * @return {Pin}
 */

Pin.prototype.low = function() {
  Pin.write(this, 0);
  this.emit("low");
  return this;
};

/**
 * read  Read from the pin, value is passed to callback continuation
 * @return {Pin}
 */

/**
 * write  Write to a pin
 * @return {Pin}
 */
["read", "write"].forEach(function(state) {
  Pin.prototype[state] = function(valOrCallback) {
    Pin[state](this, valOrCallback);
    return this;
  };
});

/**
 * mode  Set or Get the mode
 *
 * @param {Hex|String} [mode] Set the IO or command mode for this pin
 *   INPUT: 0x00
 *   OUTPUT: 0x01
 *   ANALOG: 0x02
 *   PWM: 0x03
 *   SERVO: 0x04
 *
 * @return {Pin}
 *
 * @param {undefined} Get the mode for this pin
 * @return {Hex}
 */

Pin.prototype.mode = function(mode) {
  if (mode && priv.get(this).mode !== mode) {
    // Allows setting the mode with strings, eg "INPUT", "OUTPUT"
    if (typeof mode !== "number" && modes[mode]) {
      mode = modes[mode];
    }
    this.firmata.pinMode(
      this.addr,
      priv.get(this).mode = mode
    );
    return this;
  }

  return priv.get(this).mode;
};

module.exports = Pin;
<commit_msg>pin objects emit a write event<commit_after>var Board = require("../lib/board.js"),
  Descriptor = require("descriptor"),
  __ = require("../lib/fn.js"),
  events = require("events"),
  util = require("util");

var priv = new WeakMap(),
  modes;

modes = {
  INPUT: 0x00,
  OUTPUT: 0x01,
  ANALOG: 0x02,
  PWM: 0x03,
  SERVO: 0x04
};

/**
 * Pin
 * @constructor
 *
 * @description Direct Pin access objects
 *
 * @param {Object} opts Options: pin, freq, range
 */

function Pin(opts) {
  var isPin, isAnalog, addr, type, highLow, value;

  if (!(this instanceof Pin)) {
    return new Pin(opts);
  }

  isAnalog = Pin.isAnalog(opts);

  // Initialize a Device instance on a Board
  Board.Device.call(
    this, opts = Board.Options(opts)
  );

  opts.addr = opts.addr || opts.pin;

  // Create a private side table
  priv.set(this, {
    mode: this.as || 0x00,
    last: null
  });

  isPin = typeof opts !== "object";
  addr = isPin ? opts : opts.addr;
  type = opts.type || (isAnalog ? "analog" : "digital");
  value = 0;

  // Create read-only "addr(address)" property
  Object.defineProperties(this, {
    type: new Descriptor(type),
    addr: new Descriptor(addr, "!writable"),
    value: {
      get: function() {
        return value;
      }
    }
  });

  this.firmata.pinMode(
    this.addr, priv.get(this).mode
  );

  highLow = function(state) {
    return function(data) {
      var privs, isNot, emit;

      // Update the value closure
      value = data;

      privs = priv.get(this);
      isNot = state ? "low" : "high";
      emit = state ? "high" : "low";

      if (privs.mode === modes.INPUT) {
        if (privs.last === null) {
          privs.last = isNot;
        }
        if (data === state && privs.last === isNot) {
          privs.last = emit;
          this.emit(emit);
        }
      }

      // Emit a firehose "data" event
      this.emit("data");

    }.bind(this);
  }.bind(this);

  // Debounced for noise reduction: more accurately
  // detect HIGH state.
  this.firmata[type + "Read"](
    this.addr, __.debounce(highLow(1), 50)
  );

  // No debounce to read the constant stream
  // (very noisy, only care about 0)
  this.firmata[type + "Read"](
    this.addr, highLow(0)
  );
}

util.inherits(Pin, events.EventEmitter);

/**
 * Pin.@@MODE
 *
 * Read-only constants
 * Pin.INPUT   = 0x00
 * Pin.OUTPUT  = 0x01
 * Pin.ANALOG  = 0x02
 * Pin.PWM     = 0x03
 * Pin.SERVO   = 0x04
 *
 */
Object.keys(modes).forEach(function(mode) {
  Object.defineProperty(Pin, mode, {
    value: modes[mode]
  });
});

Pin.isAnalog = function(opts) {
  if (typeof opts === "string" && Pin.isPrefixed(opts, ["I", "A"])) {
    return true;
  }

  if (typeof opts === "object") {
    return Pin.isAnalog(
      typeof opts.addr !== "undefined" ? opts.addr : opts.pin
    );
  }
};

Pin.isPrefixed = function(value, prefixes) {
  value = value[0];

  return prefixes.reduce(function(resolution, prefix) {
    if (!resolution) {
      return prefix === value;
    }
    return resolution;
  }, false);
};

Pin.write = function(pin, val) {
  // Set the correct mode (OUTPUT)
  // This will only set if it needs to be set, otherwise a no-op
  pin.mode(modes.OUTPUT);

  // Create the correct type of write command
  pin.firmata[pin.type + "Write"](pin.addr, val);

  pin.emit("write", null, val);
};

Pin.read = function(pin, callback) {
  // Set the correct mode (INPUT)
  // This will only set if it needs to be set, otherwise a no-op
  pin.mode(modes.INPUT);

  pin.firmata[pin.type + "Read"](pin.addr, function() {
    callback.apply(pin, arguments);
  });
};

// Pin.prototype.isDigital = function() {
//   return this.addr > 1;
// };

// Pin.prototype.isAnalog = function() {
//   return this.board > 1;
// };

// Pin.prototype.isPWM = function() {
// };

// Pin.prototype.isServo = function() {
// };

// Pin.prototype.isI2C = function() {
// };

// Pin.prototype.isSerial = function() {
// };

// Pin.prototype.isInterrupt = function() {
// };

// Pin.prototype.isVersion = function() {
// };

Pin.prototype.query = function(callback) {
  function handler() {
    callback(this.firmata.pins[this.addr]);
  }

  this.firmata.queryPinState(this.addr, handler.bind(this));

  return this;
};

/**
 * high  Write high/1 to the pin
 * @return {Pin}
 */

Pin.prototype.high = function() {
  var value = this.type === "analog" ? 255 : 1;
  Pin.write(this, value);
  this.emit("high");
  return this;
};

/**
 * low  Write low/0 to the pin
 * @return {Pin}
 */

Pin.prototype.low = function() {
  Pin.write(this, 0);
  this.emit("low");
  return this;
};

/**
 * read  Read from the pin, value is passed to callback continuation
 * @return {Pin}
 */

/**
 * write  Write to a pin
 * @return {Pin}
 */
["read", "write"].forEach(function(state) {
  Pin.prototype[state] = function(valOrCallback) {
    Pin[state](this, valOrCallback);
    return this;
  };
});

/**
 * mode  Set or Get the mode
 *
 * @param {Hex|String} [mode] Set the IO or command mode for this pin
 *   INPUT: 0x00
 *   OUTPUT: 0x01
 *   ANALOG: 0x02
 *   PWM: 0x03
 *   SERVO: 0x04
 *
 * @return {Pin}
 *
 * @param {undefined} Get the mode for this pin
 * @return {Hex}
 */

Pin.prototype.mode = function(mode) {
  if (mode && priv.get(this).mode !== mode) {
    // Allows setting the mode with strings, eg "INPUT", "OUTPUT"
    if (typeof mode !== "number" && modes[mode]) {
      mode = modes[mode];
    }
    this.firmata.pinMode(
      this.addr,
      priv.get(this).mode = mode
    );
    return this;
  }

  return priv.get(this).mode;
};

module.exports = Pin;
<|endoftext|>


<commit_before>const rcs = {};

module.exports = rcs;

// eslint-disable-next-line
const doRequire = name => rcs[name] = require(`./options/${name}`);

doRequire('helper');
doRequire('nameGenerator');
doRequire('selectorLibrary');
doRequire('keyframesLibrary');

// eslint-disable-next-line
rcs['replace'] = require('./replace');
<commit_msg>Refactor: entry point to ES6<commit_after>import helper from './options/helper';
import replace from './replace';
import nameGenerator from './options/nameGenerator';
import selectorLibrary from './options/selectorLibrary';
import keyframesLibrary from './options/keyframesLibrary';

export default {
  helper,
  replace,
  nameGenerator,
  selectorLibrary,
  keyframesLibrary,
};
<|endoftext|>


<commit_before>(function() {
  var Logger = require('logger-facade-nodejs'),
      util = require('util'),
      RoundRobinStrategy = require('./round_robin_strategy'),
      _ = require('lodash');

  var ServiceManagementInterface = function(configuration){

    var log = Logger.getLogger('SMI');

    var defaults = {
      // heartbeat interval in ms
      heartbeat: 1000,
      // max ttl for a service in ms
      maxTTL: 1500,
      // refresh service ttl update interval in ms
      updateInterval: 100
    };
    var config = _.defaults(configuration, defaults);

    // dictionary with sid as key and ServiceInfo array as value
    var services = { };

    // store refresh interval object id
    var onRefreshServicesTTLIntervalObj;

    // load balancing strategy
    var strategy = new RoundRobinStrategy();

    // this class is used to represent service instance info
    var ServiceInfo = function(sid, identity){
      this.sid = sid;
      this.identity = identity;
      this.ttl = config.maxTTL;

      ServiceInfo.prototype.refreshTTL = function(){
        var newTTL = this.ttl + config.heartbeat;
        this.ttl = newTTL > config.maxTTL ? config.maxTTL : newTTL;
      };

      ServiceInfo.prototype.decrementTTL = function(){
        this.ttl = this.ttl - config.updateInterval;
        return this.ttl;
      };

      ServiceInfo.prototype.isDead = function(){
        return this.ttl < 1;
      };

      ServiceInfo.prototype.toString = function(){
        return util.format("identity: %s TTL: %sms", this.identity, this.ttl);
      };
    };

    var getInstances = function(sid){
      if (!services[sid]){
        services[sid] = [];
      }

      return services[sid];
    };

    var onRefreshServicesTTL = function() {
      log.trace("SMI refreshing services TTL...");

      var decrementAndRemove = function(service){
        service.decrementTTL();
        var isDead = service.isDead();

        if (isDead) {
          log.info("SMI TTL unregister for sid: %s instance: %s!", service.sid, service.identity);
        }

        return isDead;
      };

      for(var sid in services){
        _.remove(services[sid], decrementAndRemove);
      }

      var logInstance = function(instance){
        log.trace("\t\t%s\n", instance.toString());
      };

      log.trace("SMI Service instances:");
      for(var id in services){
        log.trace("\t%s instances", id);
        services[id].forEach(logInstance);
      }
    };

    // public methods

    this.up = function(msg) {
      var to = msg.payload.toUpperCase();
      var instances = getInstances(to);
      var isRegistered = _.some(instances, { identity: msg.identity });

      if(isRegistered){
        log.warn("SMI the service instance %s is already registered!", msg.identity);

        msg.status = 500;
        return msg;
      }

      instances.push(new ServiceInfo(to, msg.identity));

      log.info("SMI register for sid: %s instance: %s!", to, msg.identity);

      msg.status = 200;
      return msg;
    };

    this.down = function(msg){
      var to = msg.payload.toUpperCase();
      var instances = getInstances(to);
      var instanceIndex = _.findIndex(instances, { identity: msg.identity });

      if(instanceIndex === -1){
        log.warn("SMI the service instance %s is not registered!", msg.identity);

        msg.status = 500;
        return msg;
      }

      instances.splice(instanceIndex, 1);

      log.info("SMI unregister for sid: %s instance %s!", to, msg.identity);

      msg.status = 200;
      return msg;
    };

    this.heartbeat = function(msg){
      var to = msg.payload.toUpperCase();
      var instances = getInstances(to);
      var instance = _.find(instances, { identity: msg.identity });

      if(!instance){
        return this.up(msg);
      }

      instance.refreshTTL();

      log.trace("SMI refreshed service instance %s TTL!", msg.identity);

      msg.status = 200;
      return msg;
    }.bind(this);

    this.getInstance = function(sid){
      var instances = getInstances(sid.toUpperCase());
      var instance = strategy.next(sid.toUpperCase(), instances);
      return instance ? instance.identity : null;
    };

    this.run = function(){
      log.debug("SMI Starting refesh routine to execute every %sms", config.updateInterval);
      onRefreshServicesTTLIntervalObj = setInterval(onRefreshServicesTTL.bind(this), config.updateInterval);

      log.info("started...");
    };

    this.stop = function(){
      log.debug("SMI Stoping refesh routine");
      clearInterval(onRefreshServicesTTLIntervalObj);
      onRefreshServicesTTLIntervalObj = null;

      log.info("stoped...");
    };
  };

  module.exports = ServiceManagementInterface;
}());
<commit_msg>remove unecessary function scoping from smi lib module<commit_after>var Logger = require('logger-facade-nodejs'),
    util = require('util'),
    RoundRobinStrategy = require('./round_robin_strategy'),
    _ = require('lodash');

var ServiceManagementInterface = function(configuration){

  var log = Logger.getLogger('SMI');

  var defaults = {
    // heartbeat interval in ms
    heartbeat: 1000,
    // max ttl for a service in ms
    maxTTL: 1500,
    // refresh service ttl update interval in ms
    updateInterval: 100
  };
  var config = _.defaults(configuration, defaults);

  // dictionary with sid as key and ServiceInfo array as value
  var services = { };

  // store refresh interval object id
  var onRefreshServicesTTLIntervalObj;

  // load balancing strategy
  var strategy = new RoundRobinStrategy();

  // this class is used to represent service instance info
  var ServiceInfo = function(sid, identity){
    this.sid = sid;
    this.identity = identity;
    this.ttl = config.maxTTL;

    ServiceInfo.prototype.refreshTTL = function(){
      var newTTL = this.ttl + config.heartbeat;
      this.ttl = newTTL > config.maxTTL ? config.maxTTL : newTTL;
    };

    ServiceInfo.prototype.decrementTTL = function(){
      this.ttl = this.ttl - config.updateInterval;
      return this.ttl;
    };

    ServiceInfo.prototype.isDead = function(){
      return this.ttl < 1;
    };

    ServiceInfo.prototype.toString = function(){
      return util.format("identity: %s TTL: %sms", this.identity, this.ttl);
    };
  };

  var getInstances = function(sid){
    if (!services[sid]){
      services[sid] = [];
    }

    return services[sid];
  };

  var onRefreshServicesTTL = function() {
    log.trace("SMI refreshing services TTL...");

    var decrementAndRemove = function(service){
      service.decrementTTL();
      var isDead = service.isDead();

      if (isDead) {
        log.info("SMI TTL unregister for sid: %s instance: %s!", service.sid, service.identity);
      }

      return isDead;
    };

    for(var sid in services){
      _.remove(services[sid], decrementAndRemove);
    }

    var logInstance = function(instance){
      log.trace("\t\t%s\n", instance.toString());
    };

    log.trace("SMI Service instances:");
    for(var id in services){
      log.trace("\t%s instances", id);
      services[id].forEach(logInstance);
    }
  };

  // public methods

  this.up = function(msg) {
    var to = msg.payload.toUpperCase();
    var instances = getInstances(to);
    var isRegistered = _.some(instances, { identity: msg.identity });

    if(isRegistered){
      log.warn("SMI the service instance %s is already registered!", msg.identity);

      msg.status = 500;
      return msg;
    }

    instances.push(new ServiceInfo(to, msg.identity));

    log.info("SMI register for sid: %s instance: %s!", to, msg.identity);

    msg.status = 200;
    return msg;
  };

  this.down = function(msg){
    var to = msg.payload.toUpperCase();
    var instances = getInstances(to);
    var instanceIndex = _.findIndex(instances, { identity: msg.identity });

    if(instanceIndex === -1){
      log.warn("SMI the service instance %s is not registered!", msg.identity);

      msg.status = 500;
      return msg;
    }

    instances.splice(instanceIndex, 1);

    log.info("SMI unregister for sid: %s instance %s!", to, msg.identity);

    msg.status = 200;
    return msg;
  };

  this.heartbeat = function(msg){
    var to = msg.payload.toUpperCase();
    var instances = getInstances(to);
    var instance = _.find(instances, { identity: msg.identity });

    if(!instance){
      return this.up(msg);
    }

    instance.refreshTTL();

    log.trace("SMI refreshed service instance %s TTL!", msg.identity);

    msg.status = 200;
    return msg;
  }.bind(this);

  this.getInstance = function(sid){
    var instances = getInstances(sid.toUpperCase());
    var instance = strategy.next(sid.toUpperCase(), instances);
    return instance ? instance.identity : null;
  };

  this.run = function(){
    log.debug("SMI Starting refesh routine to execute every %sms", config.updateInterval);
    onRefreshServicesTTLIntervalObj = setInterval(onRefreshServicesTTL.bind(this), config.updateInterval);

    log.info("started...");
  };

  this.stop = function(){
    log.debug("SMI Stoping refesh routine");
    clearInterval(onRefreshServicesTTLIntervalObj);
    onRefreshServicesTTLIntervalObj = null;

    log.info("stoped...");
  };
};

module.exports = ServiceManagementInterface;
<|endoftext|>


<commit_before>"use strict";

module.exports = WirelessTag;

var request = require('request'),
    util = require('util'),
    EventEmitter = require('events'),
    u = require('./util.js'),
    WirelessTagSensor = require('./sensor.js'),
    WirelessTagPlatform = require('./platform.js');

const roTagProps = ["uuid","slaveId","tagType","alive","rev"];
const rwTagProps = ["name",
                    ["updateInterval","postBackInterval"]];

const MAX_UPDATE_RETRIES = 2;

// the following are in milliseconds
const RETRY_UPDATE_DELAY = 5000;         // time before retrying update
const MIN_UPDATE_LOOP_WAIT = 1500;       // minimum wait time between loops
const MAX_UPDATE_LOOP_WAIT = 30 * 60000; // maximum wait time between loops
const DEFAULT_UPDATE_PADDING = 2000; // time to add to calculated update time

/*
 * The cloud instance of a Wireless Tag. One Wireless Tag manager can
 * manage multiple Wireless Tags.
 */
function WirelessTag(tagManager, tagData) {
    EventEmitter.call(this);
    this.wirelessTagManager = tagManager;
    this.errorHandler =
        tagManager ? tagManager.errorHandler : u.defaultHandler;
    this.callAPI = WirelessTagPlatform.callAPI;
    u.setObjProperties(this, roTagProps, rwTagProps);
    this.data = tagData || {};
    this.on('_data', this.bounceUpdateLoop.bind(this));
}
util.inherits(WirelessTag, EventEmitter);

WirelessTag.prototype.sensorCapabilities = function() {
    var capabilities = [];
    for (let propName in this) {
        if (propName.startsWith("has") && propName.endsWith("Sensor")) {
            let propValue = this[propName];
            if ((('function' === typeof propValue) && propValue.call(this))
                || (('function' !== typeof propValue) && propValue)) {
                capabilities.push(propName.
                                  replace(/has(\w+)Sensor/,'$1').
                                  toLowerCase());
            }
        }
    }
    return capabilities;
};

// list of positive capabilities (canXXX()) and facts (isYYY(), hasZZZ())
WirelessTag.prototype.hardwareFacts = function() {
    let facts = [];
    for (let cap in this) {
        if (cap.startsWith("can")
            || cap.startsWith("is")
            || (cap.startsWith("has") && !cap.endsWith("Sensor"))) {
            let propValue = this[cap];
            if ((('function' === typeof propValue) && propValue.call(this))
                || (('function' !== typeof propValue) && propValue)) {
                facts.push(cap);
            }
        }
    }
    return facts;
};

WirelessTag.prototype.eachSensor = function(action) {
    if (action === undefined) action = (s) => { return s };
    let capabilities = this.sensorCapabilities();
    let retVals = [];
    for (let cap of capabilities) {
        let sensor = this[cap + "Sensor"];
        if (sensor) retVals.push(action(sensor));
    }
    return retVals;
};

WirelessTag.prototype.version = function() {
    switch (this.data.version1) {
    case 2:
	switch (this.rev) {
        case 14: return "v2.1";
	case 15: return "v2.2";
	case 31: return "v2.3";
	default: return "v2.0";
        }
        break; // technically not needed since every preceding case returns
    case 3:
	return "v3.0";
    case 4:
	return "v4.0";
    default:
        if (this.tagType === 12) {
            switch (this.rev) {
            case 0: return "v1.1";
            case 1: return "v1.2";
            case 11: return "v1.3";
            case 12: return "v1.4";
            case 13: return "v1.5";
            }
        }
    }
    return "";
};

WirelessTag.prototype.hasMotionSensor = function() {
    return (this.tagType === 12 || this.tagType === 13 || this.tagType === 21);
};
WirelessTag.prototype.hasLightSensor = function() {
    return (this.tagType === 26);
};
WirelessTag.prototype.hasMoistureSensor = function() {
    return (this.tagType === 32 || this.tagType === 33);
};
WirelessTag.prototype.hasWaterSensor = function() {
    return (this.tagType === 32 || this.tagType === 33);
};
WirelessTag.prototype.hasReedSensor = function() {
    return (this.tagType === 52 || this.tagType === 53);
};
WirelessTag.prototype.hasPIRSensor = function() {
    return (this.tagType === 72);
};
WirelessTag.prototype.hasEventSensor = function() {
    return (this.hasMotionSensor()
            || this.hasLightSensor()
            || this.hasReedSensor()
            || this.hasPIRSensor());
};
WirelessTag.prototype.hasHumiditySensor = function() {
    return this.isHTU();
};
WirelessTag.prototype.hasTempSensor = function() {
    return !(this.tagType === 82 || this.tagType === 92);
};
WirelessTag.prototype.hasCurrentSensor = function() {
    return this.tagType === 42;
};
WirelessTag.prototype.hasOutOfRangeSensor = function() {
    return this.isPhysicalTag();
};
WirelessTag.prototype.hasBatterySensor = function() {
    return this.isPhysicalTag();
};
WirelessTag.prototype.hasSignalSensor = function() {
    return this.isPhysicalTag();
};
WirelessTag.prototype.hasAccelerometer = function() {
    return this.hasMotionSensor() && ((this.rev & 0x0F) == 0x0A);
};
WirelessTag.prototype.canMotionTimeout = function() {
    return this.hasMotionSensor()
        && this.rev >= 14
        && (this.tagType !== 12 || this.rev !== 15);
};
WirelessTag.prototype.canBeep = function() {
    return this.tagType === 13
        || this.tagType === 12
        || this.tagType === 21
        || this.tagType === 26;
};
WirelessTag.prototype.canPlayback = function() {
    return (this.tagType === 21);
};
WirelessTag.prototype.isHTU = function() {
    return this.tagType === 13 || this.tagType === 21 // motion && type != 12
        || this.tagType === 52                        // reed && type != 53
        || this.tagType === 26                        // ambient light
        || this.tagType === 72                        // PIR
        || this.isKumostat()
    ;
};
WirelessTag.prototype.isPhysicalTag = function() {
    return ! (this.isKumostat()
              || this.isNest()
              || this.isWeMo()
              || this.isCamera());
};
WirelessTag.prototype.isKumostat = function() {
    return (this.tagType === 62);
};
WirelessTag.prototype.isNest = function() {
    return (this.data.thermostat !== null
            && this.data.thermostat.nest_id !== null);
};
WirelessTag.prototype.isWeMo = function() {
    return (this.tagType === 82);
};
WirelessTag.prototype.isWeMoLED = function() {
    return (this.isWeMo() && (this.data.cap > 0));
};
WirelessTag.prototype.isCamera = function() {
    return (this.tagType === 92);
};

WirelessTag.prototype.lastUpdated = function() {
    return new Date(u.FILETIMEtoDate(this.data.lastComm));
};

WirelessTag.prototype.discoverSensors = function(tag) {
    if (! tag) tag = this;
    var capabilities = tag.sensorCapabilities();
    var proms = [];
    for (let cap of capabilities) {
        let propName = cap + "Sensor";
        let sensor = tag[propName];
        if (sensor) {
            proms.push(Promise.resolve(sensor));
        } else {
            let retValue = createSensor(tag, cap);
            sensor = retValue[0];
            Object.defineProperty(tag, propName, {
                enumerable: true,
                value: sensor
            });
            proms.push(retValue[1]);
        }
    }
    return Promise.all(proms);
};

function createSensor(tag, sensorType) {
    let sensor = new WirelessTagSensor(tag, sensorType);
    let req = WirelessTagSensor.loadMonitoringConfig(sensor).then(
        (config) => {
            try {
                sensor.monitoringConfig(config);
                return sensor;
            }
            finally {
                tag.emit('discover', sensor);
            }
        },
        (error) => {
            tag.emit('discover', sensor);
            if (error instanceof Error) {
                tag.errorHandler()(error);
            } else {
                console.warn(error);
            }
        }
    );
    return [sensor, req];
}

WirelessTag.prototype.update = function(tag, callback)  {
    if ((tag === undefined) || ('function' === typeof tag)) {
        callback = tag;
        tag = this;
    }
    var req = tag.callAPI(
        '/ethClient.asmx/GetTagForSlaveId',
        { slaveid: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
};

WirelessTag.prototype.liveUpdate = function(tag, callback)  {
    if (! tag) tag = this;
    var req = tag.callAPI(
        '/ethClient.asmx/RequestImmediatePostback',
        { id: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
};

WirelessTag.prototype.startUpdateLoop = function(minWait) {
    let padding = DEFAULT_UPDATE_PADDING;
    if (minWait === undefined) {
        minWait = MIN_UPDATE_LOOP_WAIT;
    } else if (minWait > MAX_UPDATE_LOOP_WAIT) {
        minWait = MAX_UPDATE_LOOP_WAIT;
    }
    if (this._updateTimer) return this._updateTimer;
    this._updateTimer = true; // placeholder to avoid race conditions
    let action = () => {
        this._updateTimer = null;
        this.update().then(
            (success) => {
                return this.startUpdateLoop();
            },
            (error) => {
                console.error(error);
                return this.startUpdateLoop(minWait * 2);
            }
        );
    };
    let remainingTime =
        (this.updateInterval * 1000) -
        (Date.now() - this.lastUpdated().getTime());
    if (remainingTime < minWait) {
        remainingTime = minWait;
    } else {
        remainingTime += padding;
    }
    // ensure that updates weren't cancelled since we entered here
    if (this._updateTimer) {
        this._updateTimer = setTimeout(action, remainingTime);
    }
    return this._updateTimer;
};

WirelessTag.prototype.stopUpdateLoop = function() {
    let timer = this._updateTimer;
    this._updateTimer = null;   // avoid race conditions
    if (timer && timer !== true) {
        clearTimeout(timer);
    }
};

WirelessTag.prototype.bounceUpdateLoop = function(tag) {
    if (! tag) tag = this;
    let timer = tag._updateTimer;
    if (timer && timer !== true) {
        console.warn("## bouncing update timer loop for tag", tag.slaveId);
        tag.stopUpdateLoop();
        tag.startUpdateLoop();
    }
};

WirelessTag.prototype.toString = function() {
    let propsObj = {
        manager : {
            name: this.wirelessTagManager.name,
            mac: this.wirelessTagManager.mac
        }
    };
    // all data properties except private ones
    for (let propName of Object.getOwnPropertyNames(this)) {
        if (! (propName.endsWith('Sensor')
               || propName.startsWith('_')
               || (propName === 'wirelessTagManager')
               || ('function' === typeof this[propName]))) {
            propsObj[propName] = this[propName];
        }
    }
    // remove domain, data, and other undesired properties picked up above
    delete propsObj.data;
    delete propsObj.domain;
    // when tag was last updated
    propsObj.lastUpdated = this.lastUpdated().toString();
    // version
    propsObj.version = this.version();
    // list of positive capabilities (canXXX()) and facts (isYYY(), hasZZZ())
    propsObj.facts = this.hardwareFacts();
    // list of sensor capabilities
    propsObj.sensors = this.sensorCapabilities();
    return JSON.stringify(propsObj);
};

WirelessTag.prototype.setUpdateInterval = function(value, callback) {
    if (value === undefined) value = this.updateInterval;
    if (('number' !== typeof value) || (value <= 0)) {
        return Promise.reject("invalid update interval for tag", this.name);
    }
    var req = this.callAPI(
        '/ethClient.asmx/SetPostbackIntervalFor',
        { id: this.slaveId, sec: value },
        callback);
    return req.then(
        (result) => {
            this.data = result;
            if (this.updateInterval != value) {
                // the API call itself succeeded, so this should resolve
                // itself if we retry updating after a short delay
                return u.delayActionPromise(
                    this.update.bind(this), RETRY_UPDATE_DELAY);
            }
            return this;
        },
        this.errorHandler(callback)
    );
};
<commit_msg>Tweaks the logic for computing timeout delay for update loop<commit_after>"use strict";

module.exports = WirelessTag;

var request = require('request'),
    util = require('util'),
    EventEmitter = require('events'),
    u = require('./util.js'),
    WirelessTagSensor = require('./sensor.js'),
    WirelessTagPlatform = require('./platform.js');

const roTagProps = ["uuid","slaveId","tagType","alive","rev"];
const rwTagProps = ["name",
                    ["updateInterval","postBackInterval"]];

const MAX_UPDATE_RETRIES = 2;

// the following are in milliseconds
const RETRY_UPDATE_DELAY = 5000;         // time before retrying update
const MIN_UPDATE_LOOP_WAIT = 3000;       // minimum wait time between loops
const MAX_UPDATE_LOOP_WAIT = 30 * 60000; // maximum wait time between loops
const CLOUD_DATA_DELAY = 55 * 1000;   // delay with which data shows up in cloud

/*
 * The cloud instance of a Wireless Tag. One Wireless Tag manager can
 * manage multiple Wireless Tags.
 */
function WirelessTag(tagManager, tagData) {
    EventEmitter.call(this);
    this.wirelessTagManager = tagManager;
    this.errorHandler =
        tagManager ? tagManager.errorHandler : u.defaultHandler;
    this.callAPI = WirelessTagPlatform.callAPI;
    u.setObjProperties(this, roTagProps, rwTagProps);
    this.data = tagData || {};
    this.on('_data', this.bounceUpdateLoop.bind(this));
}
util.inherits(WirelessTag, EventEmitter);

WirelessTag.prototype.sensorCapabilities = function() {
    var capabilities = [];
    for (let propName in this) {
        if (propName.startsWith("has") && propName.endsWith("Sensor")) {
            let propValue = this[propName];
            if ((('function' === typeof propValue) && propValue.call(this))
                || (('function' !== typeof propValue) && propValue)) {
                capabilities.push(propName.
                                  replace(/has(\w+)Sensor/,'$1').
                                  toLowerCase());
            }
        }
    }
    return capabilities;
};

// list of positive capabilities (canXXX()) and facts (isYYY(), hasZZZ())
WirelessTag.prototype.hardwareFacts = function() {
    let facts = [];
    for (let cap in this) {
        if (cap.startsWith("can")
            || cap.startsWith("is")
            || (cap.startsWith("has") && !cap.endsWith("Sensor"))) {
            let propValue = this[cap];
            if ((('function' === typeof propValue) && propValue.call(this))
                || (('function' !== typeof propValue) && propValue)) {
                facts.push(cap);
            }
        }
    }
    return facts;
};

WirelessTag.prototype.eachSensor = function(action) {
    if (action === undefined) action = (s) => { return s };
    let capabilities = this.sensorCapabilities();
    let retVals = [];
    for (let cap of capabilities) {
        let sensor = this[cap + "Sensor"];
        if (sensor) retVals.push(action(sensor));
    }
    return retVals;
};

WirelessTag.prototype.version = function() {
    switch (this.data.version1) {
    case 2:
	switch (this.rev) {
        case 14: return "v2.1";
	case 15: return "v2.2";
	case 31: return "v2.3";
	default: return "v2.0";
        }
        break; // technically not needed since every preceding case returns
    case 3:
	return "v3.0";
    case 4:
	return "v4.0";
    default:
        if (this.tagType === 12) {
            switch (this.rev) {
            case 0: return "v1.1";
            case 1: return "v1.2";
            case 11: return "v1.3";
            case 12: return "v1.4";
            case 13: return "v1.5";
            }
        }
    }
    return "";
};

WirelessTag.prototype.hasMotionSensor = function() {
    return (this.tagType === 12 || this.tagType === 13 || this.tagType === 21);
};
WirelessTag.prototype.hasLightSensor = function() {
    return (this.tagType === 26);
};
WirelessTag.prototype.hasMoistureSensor = function() {
    return (this.tagType === 32 || this.tagType === 33);
};
WirelessTag.prototype.hasWaterSensor = function() {
    return (this.tagType === 32 || this.tagType === 33);
};
WirelessTag.prototype.hasReedSensor = function() {
    return (this.tagType === 52 || this.tagType === 53);
};
WirelessTag.prototype.hasPIRSensor = function() {
    return (this.tagType === 72);
};
WirelessTag.prototype.hasEventSensor = function() {
    return (this.hasMotionSensor()
            || this.hasLightSensor()
            || this.hasReedSensor()
            || this.hasPIRSensor());
};
WirelessTag.prototype.hasHumiditySensor = function() {
    return this.isHTU();
};
WirelessTag.prototype.hasTempSensor = function() {
    return !(this.tagType === 82 || this.tagType === 92);
};
WirelessTag.prototype.hasCurrentSensor = function() {
    return this.tagType === 42;
};
WirelessTag.prototype.hasOutOfRangeSensor = function() {
    return this.isPhysicalTag();
};
WirelessTag.prototype.hasBatterySensor = function() {
    return this.isPhysicalTag();
};
WirelessTag.prototype.hasSignalSensor = function() {
    return this.isPhysicalTag();
};
WirelessTag.prototype.hasAccelerometer = function() {
    return this.hasMotionSensor() && ((this.rev & 0x0F) == 0x0A);
};
WirelessTag.prototype.canMotionTimeout = function() {
    return this.hasMotionSensor()
        && this.rev >= 14
        && (this.tagType !== 12 || this.rev !== 15);
};
WirelessTag.prototype.canBeep = function() {
    return this.tagType === 13
        || this.tagType === 12
        || this.tagType === 21
        || this.tagType === 26;
};
WirelessTag.prototype.canPlayback = function() {
    return (this.tagType === 21);
};
WirelessTag.prototype.isHTU = function() {
    return this.tagType === 13 || this.tagType === 21 // motion && type != 12
        || this.tagType === 52                        // reed && type != 53
        || this.tagType === 26                        // ambient light
        || this.tagType === 72                        // PIR
        || this.isKumostat()
    ;
};
WirelessTag.prototype.isPhysicalTag = function() {
    return ! (this.isKumostat()
              || this.isNest()
              || this.isWeMo()
              || this.isCamera());
};
WirelessTag.prototype.isKumostat = function() {
    return (this.tagType === 62);
};
WirelessTag.prototype.isNest = function() {
    return (this.data.thermostat !== null
            && this.data.thermostat.nest_id !== null);
};
WirelessTag.prototype.isWeMo = function() {
    return (this.tagType === 82);
};
WirelessTag.prototype.isWeMoLED = function() {
    return (this.isWeMo() && (this.data.cap > 0));
};
WirelessTag.prototype.isCamera = function() {
    return (this.tagType === 92);
};

WirelessTag.prototype.lastUpdated = function() {
    return new Date(u.FILETIMEtoDate(this.data.lastComm));
};

WirelessTag.prototype.discoverSensors = function(tag) {
    if (! tag) tag = this;
    let capabilities = tag.sensorCapabilities();
    let proms = [];
    for (let cap of capabilities) {
        let propName = cap + "Sensor";
        let sensor = tag[propName];
        if (sensor) {
            proms.push(Promise.resolve(sensor));
        } else {
            let retValue = createSensor(tag, cap);
            sensor = retValue[0];
            Object.defineProperty(tag, propName, {
                enumerable: true,
                value: sensor
            });
            proms.push(retValue[1]);
        }
    }
    return Promise.all(proms);
};

function createSensor(tag, sensorType) {
    let sensor = new WirelessTagSensor(tag, sensorType);
    let req = WirelessTagSensor.loadMonitoringConfig(sensor).then(
        (config) => {
            try {
                sensor.monitoringConfig(config);
                return sensor;
            }
            finally {
                tag.emit('discover', sensor);
            }
        },
        (error) => {
            tag.emit('discover', sensor);
            if (error instanceof Error) {
                tag.errorHandler()(error);
            } else {
                console.warn(error);
            }
        }
    );
    return [sensor, req];
}

WirelessTag.prototype.update = function(tag, callback)  {
    if ((tag === undefined) || ('function' === typeof tag)) {
        callback = tag;
        tag = this;
    }
    var req = tag.callAPI(
        '/ethClient.asmx/GetTagForSlaveId',
        { slaveid: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
};

WirelessTag.prototype.liveUpdate = function(tag, callback)  {
    if (! tag) tag = this;
    var req = tag.callAPI(
        '/ethClient.asmx/RequestImmediatePostback',
        { id: tag.slaveId },
        callback);
    return req.then(
        (result) => {
            tag.data = result;
            return tag;
        },
        tag.errorHandler(callback)
    );
};

WirelessTag.prototype.startUpdateLoop = function(minWait) {
    if (minWait === undefined) {
        minWait = MIN_UPDATE_LOOP_WAIT;
    } else if (minWait > MAX_UPDATE_LOOP_WAIT) {
        minWait = MAX_UPDATE_LOOP_WAIT;
    }
    if (this._updateTimer) return this._updateTimer;
    this._updateTimer = true; // placeholder to avoid race conditions
    let action = () => {
        this._updateTimer = null;
        this.update().then(
            (success) => {
                return this.startUpdateLoop();
            },
            (error) => {
                console.error(error);
                return this.startUpdateLoop(minWait * 2);
            }
        );
    };
    let timeNextExpected =
        this.lastUpdated().getTime()
        + (this.updateInterval * 1000)
        + CLOUD_DATA_DELAY;
    let remainingTime = timeNextExpected - Date.now();
    if (remainingTime < minWait) {
        remainingTime = minWait;
    }
    // ensure that updates weren't cancelled since we entered here
    if (this._updateTimer === true) {
        this._updateTimer = setTimeout(action, remainingTime);
    }
    return this._updateTimer;
};

WirelessTag.prototype.stopUpdateLoop = function() {
    let timer = this._updateTimer;
    this._updateTimer = null;   // avoid race conditions
    if (timer && timer !== true) {
        clearTimeout(timer);
    }
};

WirelessTag.prototype.bounceUpdateLoop = function(tag) {
    if (! tag) tag = this;
    let timer = tag._updateTimer;
    if (timer && timer !== true) {
        console.warn("## bouncing update timer loop for tag", tag.slaveId);
        tag.stopUpdateLoop();
        tag.startUpdateLoop();
    }
};

WirelessTag.prototype.toString = function() {
    let propsObj = {
        manager : {
            name: this.wirelessTagManager.name,
            mac: this.wirelessTagManager.mac
        }
    };
    // all data properties except private ones
    for (let propName of Object.getOwnPropertyNames(this)) {
        if (! (propName.endsWith('Sensor')
               || propName.startsWith('_')
               || (propName === 'wirelessTagManager')
               || ('function' === typeof this[propName]))) {
            propsObj[propName] = this[propName];
        }
    }
    // remove domain, data, and other undesired properties picked up above
    delete propsObj.data;
    delete propsObj.domain;
    // when tag was last updated
    propsObj.lastUpdated = this.lastUpdated().toString();
    // version
    propsObj.version = this.version();
    // list of positive capabilities (canXXX()) and facts (isYYY(), hasZZZ())
    propsObj.facts = this.hardwareFacts();
    // list of sensor capabilities
    propsObj.sensors = this.sensorCapabilities();
    return JSON.stringify(propsObj);
};

WirelessTag.prototype.setUpdateInterval = function(value, callback) {
    if (value === undefined) value = this.updateInterval;
    if (('number' !== typeof value) || (value <= 0)) {
        return Promise.reject("invalid update interval for tag", this.name);
    }
    var req = this.callAPI(
        '/ethClient.asmx/SetPostbackIntervalFor',
        { id: this.slaveId, sec: value },
        callback);
    return req.then(
        (result) => {
            this.data = result;
            if (this.updateInterval != value) {
                // the API call itself succeeded, so this should resolve
                // itself if we retry updating after a short delay
                return u.delayActionPromise(
                    this.update.bind(this), RETRY_UPDATE_DELAY);
            }
            return this;
        },
        this.errorHandler(callback)
    );
};
<|endoftext|>


<commit_before>var logger = require('./logger'),
    env = require('./env'),
    Packer = require('./fstream-jam'),
    fstream = require('fstream'),
    tar = require('tar'),
    zlib = require('zlib'),
    path = require('path'),
    fs = require('fs');

var umask = parseInt(022, 8);
var modes = {
    exec: 0777 & (~umask),
    file: 0666 & (~umask),
    umask: umask
};

var myUid = process.getuid && process.getuid(),
    myGid = process.getgid && process.getgid();

if (process.env.SUDO_UID && myUid === 0) {
    if (!isNaN(process.env.SUDO_UID)) {
        myUid = +process.env.SUDO_UID;
    }
    if (!isNaN(process.env.SUDO_GID)) {
        myGid = +process.env.SUDO_GID;
    }
}

/**
 */

exports.create = function(source, target, callback) {

    function returnError(err) {
        // don't call the callback multiple times, just return the first error
        var _callback = callback;
        callback = function () {};
        return _callback(err);
    }

    var fwriter = fstream.Writer({ type: 'File', path: target });
    fwriter.on('error', function (err) {
        logger.error('error writing ' + target);
        //logger.error(err);
        return returnError(err);
    });
    fwriter.on('close', function () {
        callback(null, target);
    });

    var istream = Packer({
        path: source,
        type: "Directory",
        isDirectory: true
    });
    istream.on('error', function (err) {
        logger.error('error reading ' + source);
        //logger.error(err);
        return returnError(err);
    });
    istream.on("child", function (c) {
        var root = path.resolve(c.root.path, '../package');
        logger.info('adding', c.path.substr(root.length + 1));
    });

    var packer = tar.Pack({ noProprietary: true });
    packer.on('error', function (err) {
        logger.error('tar creation error ' + target);
        //logger.error(err);
        return returnError(err);
    });

    var zipper = zlib.Gzip();
    zipper.on('error', function (err) {
        logger.error('gzip error ' + target);
        //logger.error(err);
        return returnError(err);
    });

    istream.pipe(packer).pipe(zipper).pipe(fwriter);
};

/**
 */

exports.extract = function (source, target, callback) {

    var umask = modes.umask;
    var dmode = modes.dmode;
    var fmode = modes.fmode;

    function returnError(err) {
        // don't call the callback multiple times, just return the first error
        var _callback = callback;
        callback = function () {};
        return _callback(err);
    }

    var freader = fs.createReadStream(source);
    freader.on('error', function (err) {
        logger.error('error reading ' + source);
        //logger.error(err);
        return returnError(err);
    });

    var extract_opts = {
        type: 'Directory',
        path: target,
        strip: 1,
        filter: function () {
            // symbolic links are not allowed in packages
            if (this.type.match(/^.*Link$/)) {
                logger.warning(
                    'excluding symbolic link',
                    this.path.substr(target.length + 1) + ' -> ' + this.linkpath
                );
                return false;
            }
            return true;
        }
    };
    if (!env.isWindows && typeof uid === "number" && typeof gid === "number") {
        extract_opts.uid = myUid;
        extract_opts.gid = myGid;
    }
    var extractor = tar.Extract(extract_opts);
    extractor.on('error', function (err) {
        logger.error('untar error ' + source);
        //logger.error(err);
        return returnError(err);
    });
    extractor.on('entry', function (entry) {
        logger.info('extracting', entry.path);
        entry.mode = entry.mode || entry.props.mode;
        var original_mode = entry.mode;
        entry.mode = entry.mode | (entry.type === "Directory" ? dmode: fmode);
        entry.mode = entry.mode & (~umask);
        if (original_mode !== entry.mode) {
            logger.info(
                'modified mode',
                original_mode + ' => ' + entry.mode + ' ' + entry.path
            );
        }

        if (process.platform !== "win32" &&
            typeof uid === "number" && typeof gid === "number") {
            entry.props.uid = entry.uid = uid;
            entry.props.gid = entry.gid = gid;
        }
    });
    extractor.on('end', function () {
        return callback(null, target);
    });

    var unzipper = zlib.Unzip();
    unzipper.on('error', function (err) {
        logger.error('unzip error ' + source);
        //logger.error(err);
        return returnError(err);
    });

    freader.pipe(unzipper).pipe(extractor);
};
<commit_msg>fix typo in typeof check for uid and gid<commit_after>var logger = require('./logger'),
    env = require('./env'),
    Packer = require('./fstream-jam'),
    fstream = require('fstream'),
    tar = require('tar'),
    zlib = require('zlib'),
    path = require('path'),
    fs = require('fs');

var umask = parseInt(022, 8);
var modes = {
    exec: 0777 & (~umask),
    file: 0666 & (~umask),
    umask: umask
};

var myUid = process.getuid && process.getuid(),
    myGid = process.getgid && process.getgid();

if (process.env.SUDO_UID && myUid === 0) {
    if (!isNaN(process.env.SUDO_UID)) {
        myUid = +process.env.SUDO_UID;
    }
    if (!isNaN(process.env.SUDO_GID)) {
        myGid = +process.env.SUDO_GID;
    }
}

/**
 */

exports.create = function(source, target, callback) {

    function returnError(err) {
        // don't call the callback multiple times, just return the first error
        var _callback = callback;
        callback = function () {};
        return _callback(err);
    }

    var fwriter = fstream.Writer({ type: 'File', path: target });
    fwriter.on('error', function (err) {
        logger.error('error writing ' + target);
        //logger.error(err);
        return returnError(err);
    });
    fwriter.on('close', function () {
        callback(null, target);
    });

    var istream = Packer({
        path: source,
        type: "Directory",
        isDirectory: true
    });
    istream.on('error', function (err) {
        logger.error('error reading ' + source);
        //logger.error(err);
        return returnError(err);
    });
    istream.on("child", function (c) {
        var root = path.resolve(c.root.path, '../package');
        logger.info('adding', c.path.substr(root.length + 1));
    });

    var packer = tar.Pack({ noProprietary: true });
    packer.on('error', function (err) {
        logger.error('tar creation error ' + target);
        //logger.error(err);
        return returnError(err);
    });

    var zipper = zlib.Gzip();
    zipper.on('error', function (err) {
        logger.error('gzip error ' + target);
        //logger.error(err);
        return returnError(err);
    });

    istream.pipe(packer).pipe(zipper).pipe(fwriter);
};

/**
 */

exports.extract = function (source, target, callback) {

    var umask = modes.umask;
    var dmode = modes.dmode;
    var fmode = modes.fmode;

    function returnError(err) {
        // don't call the callback multiple times, just return the first error
        var _callback = callback;
        callback = function () {};
        return _callback(err);
    }

    var freader = fs.createReadStream(source);
    freader.on('error', function (err) {
        logger.error('error reading ' + source);
        //logger.error(err);
        return returnError(err);
    });

    var extract_opts = {
        type: 'Directory',
        path: target,
        strip: 1,
        filter: function () {
            // symbolic links are not allowed in packages
            if (this.type.match(/^.*Link$/)) {
                logger.warning(
                    'excluding symbolic link',
                    this.path.substr(target.length + 1) + ' -> ' + this.linkpath
                );
                return false;
            }
            return true;
        }
    };
    if (!env.isWindows && typeof myUid === "number" && typeof myGid === "number") {
        extract_opts.uid = myUid;
        extract_opts.gid = myGid;
    }
    var extractor = tar.Extract(extract_opts);
    extractor.on('error', function (err) {
        logger.error('untar error ' + source);
        //logger.error(err);
        return returnError(err);
    });
    extractor.on('entry', function (entry) {
        logger.info('extracting', entry.path);
        entry.mode = entry.mode || entry.props.mode;
        var original_mode = entry.mode;
        entry.mode = entry.mode | (entry.type === "Directory" ? dmode: fmode);
        entry.mode = entry.mode & (~umask);
        if (original_mode !== entry.mode) {
            logger.info(
                'modified mode',
                original_mode + ' => ' + entry.mode + ' ' + entry.path
            );
        }

        if (process.platform !== "win32" &&
            typeof myUid === "number" && typeof myGid === "number") {
            entry.props.uid = entry.uid = myUid;
            entry.props.gid = entry.gid = myGid;
        }
    });
    extractor.on('end', function () {
        return callback(null, target);
    });

    var unzipper = zlib.Unzip();
    unzipper.on('error', function (err) {
        logger.error('unzip error ' + source);
        //logger.error(err);
        return returnError(err);
    });

    freader.pipe(unzipper).pipe(extractor);
};
<|endoftext|>


<commit_before>var spawn = require('child_process').spawn,
    logger = require('./logger');

exports.create = function (outfile, path, callback) {
    tar = spawn('tar', ['-czvf'].concat([outfile, path]));

    var err_messages = '';

    tar.stdout.on('data', function (data) {
        data.toString().split('\n').forEach(function (p) {
            if (p && p !== path && p !== path + '/') {
                logger.info('packing', p);
            }
        });
    });
    tar.stderr.on('data', function (data) {
        err_messages += data;
    });

    tar.on('exit', function (code) {
        if (code !== 0) {
            return callback(new Error(err_messages));
        }
        return callback();
    });
};
<commit_msg>add package files to package directory when creating tar.gz archive<commit_after>var spawn = require('child_process').spawn,
    logger = require('./logger'),
    utils = require('./utils');

exports.create = function (outfile, path, callback) {

    var outpath = utils.abspath(outfile, process.cwd());
    var args = ['-czvf', outpath, '--transform', 's/^\./package/', '.'];
    console.log('tar' + args.join(' '));
    tar = spawn('tar', args, {cwd: path});

    var err_messages = '';

    tar.stdout.on('data', function (data) {
        data.toString().split('\n').forEach(function (p) {
            p = p.replace(/^\.\//, '');
            if (p) {
                logger.info('packing', p);
            }
        });
    });
    tar.stderr.on('data', function (data) {
        err_messages += data;
    });

    tar.on('exit', function (code) {
        if (code !== 0) {
            return callback(new Error(err_messages));
        }
        return callback();
    });
};
<|endoftext|>


<commit_before>/*!
 * Tmp
 *
 * Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu>
 *
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var
  fs     = require('fs'),
  path   = require('path'),
  os     = require('os'),
  crypto = require('crypto'),
  exists = fs.exists || path.exists,
  existsSync = fs.existsSync || path.existsSync,
  tmpDir = require('os-tmpdir'),
  _c     = require('constants');

/**
 * The working inner variables.
 */
var
  // store the actual TMP directory
  _TMP = tmpDir(),

  // the random characters to choose from
  RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',

  TEMPLATE_PATTERN = /XXXXXX/,

  DEFAULT_TRIES = 3,

  CREATE_FLAGS = _c.O_CREAT | _c.O_EXCL | _c.O_RDWR,

  DIR_MODE = 448 /* 0700 */,
  FILE_MODE = 384 /* 0600 */,

  // this will hold the objects need to be removed on exit
  _removeObjects = [],

  _gracefulCleanup = false,
  _uncaughtException = false;

/**
 * Random name generator based on crypto.
 * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
 *
 * @param {Number} howMany
 * @return {String}
 * @api private
 */
function _randomChars(howMany) {
  var
    value = [],
    rnd = null;

  // make sure that we do not fail because we ran out of entropy
  try {
    rnd = crypto.randomBytes(howMany);
  } catch (e) {
    rnd = crypto.pseudoRandomBytes(howMany);
  }

  for (var i = 0; i < howMany; i++) {
    value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
  }

  return value.join('');
}

/**
 * Checks whether the `obj` parameter is defined or not.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */
function _isUndefined(obj) {
  return typeof obj === 'undefined';
}

/**
 * Parses the function arguments.
 *
 * This function helps to have optional arguments.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _parseArguments(options, callback) {
  if (typeof options == 'function') {
    var
      tmp = options;
      options = callback || {};
      callback = tmp;
  } else if (typeof options == 'undefined') {
    options = {};
  }

  return [options, callback];
}

/**
 * Generates a new temporary name.
 *
 * @param {Object} opts
 * @returns {String}
 * @api private
 */
function _generateTmpName(opts) {
  if (opts.name) {
    return path.join(opts.dir || _TMP, opts.name);
  }

  // mkstemps like template
  if (opts.template) {
    return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
  }

  // prefix and postfix
  var name = [
    opts.prefix || 'tmp-',
    process.pid,
    _randomChars(12),
    opts.postfix || ''
  ].join('');

  return path.join(opts.dir || _TMP, name);
}

/**
 * Gets a temporary file name.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _getTmpName(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    return cb(new Error('Invalid tries'));

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    return cb(new Error('Invalid template provided'));

  (function _getUniqueName() {
    var name = _generateTmpName(opts);

    // check whether the path exists then retry if needed
    exists(name, function _pathExists(pathExists) {
      if (pathExists) {
        if (tries-- > 0) return _getUniqueName();

        return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
      }

      cb(null, name);
    });
  }());
}

/**
 * Synchronous version of _getTmpName.
 *
 * @param {Object} options
 * @returns {String}
 * @api private
 */
function _getTmpNameSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    throw new Error('Invalid tries');

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    throw new Error('Invalid template provided');

  do {
    var name = _generateTmpName(opts);
    if (!existsSync(name)) {
      return name;
    }
  } while (tries-- > 0);

  throw new Error('Could not get a unique tmp filename, max tries reached');
}

/**
 * Creates and opens a temporary file.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpFile(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

    opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create and open the file
    fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
      if (err) return cb(err);

      cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpFile.
 *
 * @param {Object} options
 * @returns {Object} object consists of name, fd and removeCallback
 * @api private
 */
function _createTmpFileSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

    opts.postfix = opts.postfix || '.tmp';

  var name = _getTmpNameSync(opts);
  var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);

  return {
    name : name,
    fd : fd,
    removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
  };
}

/**
 * Removes files and folders in a directory recursively.
 *
 * @param {String} root
 * @api private
 */
function _rmdirRecursiveSync(root) {
  var dirs = [root];

  do {
    var
      dir = dirs.pop(),
      deferred = false,
      files = fs.readdirSync(dir);

    for (var i = 0, length = files.length; i < length; i++) {
      var
        file = path.join(dir, files[i]),
        stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories

      if (stat.isDirectory()) {
        if (!deferred) {
          deferred = true;
          dirs.push(dir);
        }
        dirs.push(file);
      } else {
        fs.unlinkSync(file);
      }
    }

    if (!deferred) {
      fs.rmdirSync(dir);
    }
  } while (dirs.length !== 0);
}

/**
 * Creates a temporary directory.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpDir(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create the directory
    fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
      if (err) return cb(err);

      cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpDir.
 *
 * @param {Object} options
 * @returns {Object} object consists of name and removeCallback
 * @api private
 */
function _createTmpDirSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

  var name = _getTmpNameSync(opts);
  fs.mkdirSync(name, opts.mode || DIR_MODE);

  return {
    name : name,
    removeCallback : _prepareTmpDirRemoveCallback(name, opts)
  };
}

/**
 * Prepares the callback for removal of the temporary file.
 *
 * @param {String} name
 * @param {int} fd
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpFileRemoveCallback(name, fd, opts) {
  var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
    try {
      fs.closeSync(fdPath[0]);
    }
    catch (e) {
      // under some node/windows related circumstances, a temporary file
      // may have not be created as expected or the file was already closed
      // by the user, in which case we will simply ignore the error
      if (e.errno != -_c.EBADF && e.errno != -c.ENOENT) {
        // reraise any unanticipated error
        throw e;
      }
    }
    fs.unlinkSync(fdPath[1]);
  }, [fd, name]);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Prepares the callback for removal of the temporary directory.
 *
 * @param {String} name
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpDirRemoveCallback(name, opts) {
  var removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
  var removeCallback = _prepareRemoveCallback(removeFunction, name);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Creates a guarded function wrapping the removeFunction call.
 *
 * @param {Function} removeFunction
 * @param {Object} arg
 * @returns {Function}
 * @api private
 */
function _prepareRemoveCallback(removeFunction, arg) {
  var called = false;

  return function _cleanupCallback() {
    if (called) return;

    var index = _removeObjects.indexOf(removeFunction);
    if (index >= 0) {
      _removeObjects.splice(index, 1);
    }

    called = true;
    removeFunction(arg);
  };
}

/**
 * The garbage collector.
 *
 * @api private
 */
function _garbageCollector() {
  if (_uncaughtException && !_gracefulCleanup) {
    return;
  }

  for (var i = 0, length = _removeObjects.length; i < length; i++) {
    try {
      _removeObjects[i].call(null);
    } catch (e) {
      // already removed?
    }
  }
}

function _setGracefulCleanup() {
  _gracefulCleanup = true;
}

var version = process.versions.node.split('.').map(function (value) {
  return parseInt(value, 10);
});

if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
  process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
    _uncaughtException = true;
    _garbageCollector();

    throw err;
  });
}

process.addListener('exit', function _exit(code) {
  if (code) _uncaughtException = true;
  _garbageCollector();
});

// exporting all the needed methods
module.exports.tmpdir = _TMP;
module.exports.dir = _createTmpDir;
module.exports.dirSync = _createTmpDirSync;
module.exports.file = _createTmpFile;
module.exports.fileSync = _createTmpFileSync;
module.exports.tmpName = _getTmpName;
module.exports.tmpNameSync = _getTmpNameSync;
module.exports.setGracefulCleanup = _setGracefulCleanup;
<commit_msg>Remove trailing whitespace<commit_after>/*!
 * Tmp
 *
 * Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu>
 *
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var
  fs     = require('fs'),
  path   = require('path'),
  os     = require('os'),
  crypto = require('crypto'),
  exists = fs.exists || path.exists,
  existsSync = fs.existsSync || path.existsSync,
  tmpDir = require('os-tmpdir'),
  _c     = require('constants');

/**
 * The working inner variables.
 */
var
  // store the actual TMP directory
  _TMP = tmpDir(),

  // the random characters to choose from
  RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',

  TEMPLATE_PATTERN = /XXXXXX/,

  DEFAULT_TRIES = 3,

  CREATE_FLAGS = _c.O_CREAT | _c.O_EXCL | _c.O_RDWR,

  DIR_MODE = 448 /* 0700 */,
  FILE_MODE = 384 /* 0600 */,

  // this will hold the objects need to be removed on exit
  _removeObjects = [],

  _gracefulCleanup = false,
  _uncaughtException = false;

/**
 * Random name generator based on crypto.
 * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
 *
 * @param {Number} howMany
 * @return {String}
 * @api private
 */
function _randomChars(howMany) {
  var
    value = [],
    rnd = null;

  // make sure that we do not fail because we ran out of entropy
  try {
    rnd = crypto.randomBytes(howMany);
  } catch (e) {
    rnd = crypto.pseudoRandomBytes(howMany);
  }

  for (var i = 0; i < howMany; i++) {
    value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
  }

  return value.join('');
}

/**
 * Checks whether the `obj` parameter is defined or not.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */
function _isUndefined(obj) {
  return typeof obj === 'undefined';
}

/**
 * Parses the function arguments.
 *
 * This function helps to have optional arguments.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _parseArguments(options, callback) {
  if (typeof options == 'function') {
    var
      tmp = options;
      options = callback || {};
      callback = tmp;
  } else if (typeof options == 'undefined') {
    options = {};
  }

  return [options, callback];
}

/**
 * Generates a new temporary name.
 *
 * @param {Object} opts
 * @returns {String}
 * @api private
 */
function _generateTmpName(opts) {
  if (opts.name) {
    return path.join(opts.dir || _TMP, opts.name);
  }

  // mkstemps like template
  if (opts.template) {
    return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
  }

  // prefix and postfix
  var name = [
    opts.prefix || 'tmp-',
    process.pid,
    _randomChars(12),
    opts.postfix || ''
  ].join('');

  return path.join(opts.dir || _TMP, name);
}

/**
 * Gets a temporary file name.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _getTmpName(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    return cb(new Error('Invalid tries'));

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    return cb(new Error('Invalid template provided'));

  (function _getUniqueName() {
    var name = _generateTmpName(opts);

    // check whether the path exists then retry if needed
    exists(name, function _pathExists(pathExists) {
      if (pathExists) {
        if (tries-- > 0) return _getUniqueName();

        return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
      }

      cb(null, name);
    });
  }());
}

/**
 * Synchronous version of _getTmpName.
 *
 * @param {Object} options
 * @returns {String}
 * @api private
 */
function _getTmpNameSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    throw new Error('Invalid tries');

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    throw new Error('Invalid template provided');

  do {
    var name = _generateTmpName(opts);
    if (!existsSync(name)) {
      return name;
    }
  } while (tries-- > 0);

  throw new Error('Could not get a unique tmp filename, max tries reached');
}

/**
 * Creates and opens a temporary file.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpFile(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

    opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create and open the file
    fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
      if (err) return cb(err);

      cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpFile.
 *
 * @param {Object} options
 * @returns {Object} object consists of name, fd and removeCallback
 * @api private
 */
function _createTmpFileSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

    opts.postfix = opts.postfix || '.tmp';

  var name = _getTmpNameSync(opts);
  var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);

  return {
    name : name,
    fd : fd,
    removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
  };
}

/**
 * Removes files and folders in a directory recursively.
 *
 * @param {String} root
 * @api private
 */
function _rmdirRecursiveSync(root) {
  var dirs = [root];

  do {
    var
      dir = dirs.pop(),
      deferred = false,
      files = fs.readdirSync(dir);

    for (var i = 0, length = files.length; i < length; i++) {
      var
        file = path.join(dir, files[i]),
        stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories

      if (stat.isDirectory()) {
        if (!deferred) {
          deferred = true;
          dirs.push(dir);
        }
        dirs.push(file);
      } else {
        fs.unlinkSync(file);
      }
    }

    if (!deferred) {
      fs.rmdirSync(dir);
    }
  } while (dirs.length !== 0);
}

/**
 * Creates a temporary directory.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpDir(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create the directory
    fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
      if (err) return cb(err);

      cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpDir.
 *
 * @param {Object} options
 * @returns {Object} object consists of name and removeCallback
 * @api private
 */
function _createTmpDirSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

  var name = _getTmpNameSync(opts);
  fs.mkdirSync(name, opts.mode || DIR_MODE);

  return {
    name : name,
    removeCallback : _prepareTmpDirRemoveCallback(name, opts)
  };
}

/**
 * Prepares the callback for removal of the temporary file.
 *
 * @param {String} name
 * @param {int} fd
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpFileRemoveCallback(name, fd, opts) {
  var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
    try {
      fs.closeSync(fdPath[0]);
    }
    catch (e) {
      // under some node/windows related circumstances, a temporary file
      // may have not be created as expected or the file was already closed
      // by the user, in which case we will simply ignore the error
      if (e.errno != -_c.EBADF && e.errno != -c.ENOENT) {
        // reraise any unanticipated error
        throw e;
      }
    }
    fs.unlinkSync(fdPath[1]);
  }, [fd, name]);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Prepares the callback for removal of the temporary directory.
 *
 * @param {String} name
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpDirRemoveCallback(name, opts) {
  var removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
  var removeCallback = _prepareRemoveCallback(removeFunction, name);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Creates a guarded function wrapping the removeFunction call.
 *
 * @param {Function} removeFunction
 * @param {Object} arg
 * @returns {Function}
 * @api private
 */
function _prepareRemoveCallback(removeFunction, arg) {
  var called = false;

  return function _cleanupCallback() {
    if (called) return;

    var index = _removeObjects.indexOf(removeFunction);
    if (index >= 0) {
      _removeObjects.splice(index, 1);
    }

    called = true;
    removeFunction(arg);
  };
}

/**
 * The garbage collector.
 *
 * @api private
 */
function _garbageCollector() {
  if (_uncaughtException && !_gracefulCleanup) {
    return;
  }

  for (var i = 0, length = _removeObjects.length; i < length; i++) {
    try {
      _removeObjects[i].call(null);
    } catch (e) {
      // already removed?
    }
  }
}

function _setGracefulCleanup() {
  _gracefulCleanup = true;
}

var version = process.versions.node.split('.').map(function (value) {
  return parseInt(value, 10);
});

if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
  process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
    _uncaughtException = true;
    _garbageCollector();

    throw err;
  });
}

process.addListener('exit', function _exit(code) {
  if (code) _uncaughtException = true;
  _garbageCollector();
});

// exporting all the needed methods
module.exports.tmpdir = _TMP;
module.exports.dir = _createTmpDir;
module.exports.dirSync = _createTmpDirSync;
module.exports.file = _createTmpFile;
module.exports.fileSync = _createTmpFileSync;
module.exports.tmpName = _getTmpName;
module.exports.tmpNameSync = _getTmpNameSync;
module.exports.setGracefulCleanup = _setGracefulCleanup;
<|endoftext|>


<commit_before>/*
 * toc.js
 *
 * This is what ends up generating the table of contents.
 *
 */

var table = exports;

var smith     = require("./blacksmith"),
    fs       = require('fs'),
    fs2      = require('./fs2'),
    path     = require('path'),
    Hash     = require('hashish'),
    traverse = require('traverse'),
    helpers  = require('./helpers');

// This helper assists in building a Table of Contents.
table.buildToc = function () {

  // TODO: Instead of hard-coding the source folder, we should instead use
  // smith.src. However, given that we're not supporting multi-site, this is
  // fine.
  var src = "./pages",
      _articles;

  // Get a list of directories.
  // TODO: Can this pull from smith.content.content ?
  _articles = fs2.readDirSync(src, true, function (a) {
    return fs.statSync(a).isDirectory();
  });

  // The directory list does not include the top level. This is important if
  // there is no nested content.
  _articles[src] = { "isDirectory": true };

  // In this case, .isDirectory adds an unneeded layer to our tree so instances
  // of .isDirectory are filtered out here.
  Object.keys(_articles).forEach(function (i) {
    if (_articles[i].isDirectory) {
      _articles[i] = null;
    }
  });

  // Generate a tree.
  var toc = fs2.dirToTree(_articles),
      parent = "";

  // Order the tree.
  toc = fs2.orderedTree(toc);

  // Sort the tree.
  toc = table.sort(toc);

  // Dig until there is a layer with >1 item in it. This way, the ToC doesn't
  // have a single item in it, with every relevant listing underneath it.
  while (toc.length === 1) {
    var row = toc[0];
    var key = Object.keys(row)[0];

    // This is so that links in the ToC are still correct.
    parent += ("/"+key);

    toc = toc[0][key];
  }

  // Convert the orderedTree into HTML.
  return helpers.treeToHTML(toc, parent);
}

// This helper sorts a table of contents structure.
table.sort = function (toc) {

  // Map over the ToC recursively.
  return toc.map(function sorter (row, paths) {

    // Each row in the ToC has the form:
    //
    //     { "key": subtoc }
    //
    var paths = paths || [],
        key = Object.keys(row)[0],
        subtoc = row[key],
        filename = path.resolve("./"+paths.concat([[key]]).join("/")),
        order;

    paths = paths.concat([[key]]);

    // Uncomment this for a pretty directory view.
    // console.log(Array(paths.length+1).join(Array(3).join(" ")) + "-"+paths[paths.length-1]);

    // JSON.stringify(subtoc, true, 2).split('\n').forEach(function (l) { console.log(l); });

    // Sort the toc using various prioritized heuristics.
    [
      "bySpecified",
      "byDate"
    ].forEach(function (f) {
      subtoc = require("./toc/sorters")[f].call(null, subtoc, filename);
    });

    if (Array.isArray(subtoc)) {
      // Map over the array.
      return subtoc.map(function (row) {
        var newRow = {};
        var key = Object.keys(row)[0];

        newRow[key] = sorter(row, paths);
        return newRow;
      });
    } else {
      return subtoc;
    }

  })[0]; // Note that the end result has a list inside of a list.
};
<commit_msg>[comment] Note to self<commit_after>/*
 * toc.js
 *
 * This is what ends up generating the table of contents.
 *
 */

var table = exports;

var smith    = require("./blacksmith"),
    fs       = require('fs'),
    fs2      = require('./fs2'),
    path     = require('path'),
    Hash     = require('hashish'),
    traverse = require('traverse'),
    helpers  = require('./helpers');

// This helper assists in building a Table of Contents.
table.buildToc = function (options) {

  // TODO: Instead of hard-coding the source folder, we should instead use
  // smith.src. However, given that we're not supporting multi-site, this is
  // fine for now.
  var src = "./pages",
      _articles;

  // Get a list of directories.
  // TODO: Ideally, we would use smith.content.content, but that hasn't been
  // loaded yet!
  _articles = fs2.readDirSync(src, true, function (a) {
    return fs.statSync(a).isDirectory();
  });

  // The directory list does not include the top level. This is important if
  // there is no nested content.
  _articles[src] = { "isDirectory": true };

  // In this case, .isDirectory adds an unneeded layer to our tree so instances
  // of .isDirectory are filtered out here.
  Object.keys(_articles).forEach(function (i) {
    if (_articles[i].isDirectory) {
      _articles[i] = null;
    }
  });

  // Generate a tree.
  var toc = fs2.dirToTree(_articles),
      parent = "";

  // Order the tree.
  toc = fs2.orderedTree(toc);

  // Sort the tree.
  toc = table.sort(toc);

  // Dig until there is a layer with >1 item in it. This way, the ToC doesn't
  // have a single item in it, with every relevant listing underneath it.
  while (toc.length === 1) {
    var row = toc[0];
    var key = Object.keys(row)[0];

    // This is so that links in the ToC are still correct.
    parent += ("/"+key);

    toc = toc[0][key];
  }

  // Convert the orderedTree into HTML.
  return helpers.treeToHTML(toc, parent);
}

// This helper sorts a table of contents structure.
table.sort = function (toc) {

  // Map over the ToC recursively.
  return toc.map(function sorter (row, paths) {

    // Each row in the ToC has the form:
    //
    //     { "key": subtoc }
    //
    var paths = paths || [],
        key = Object.keys(row)[0],
        subtoc = row[key],
        filename = path.resolve("./"+paths.concat([[key]]).join("/")),
        order;

    paths = paths.concat([[key]]);

    // Uncomment this for a pretty directory view.
    // console.log(Array(paths.length+1).join(Array(3).join(" ")) + "-"+paths[paths.length-1]);

    // JSON.stringify(subtoc, true, 2).split('\n').forEach(function (l) { console.log(l); });

    // Sort the toc using various prioritized heuristics.
    [
      "bySpecified",
      "byDate"
    ].forEach(function (f) {
      subtoc = require("./toc/sorters")[f].call(null, subtoc, filename);
    });

    if (Array.isArray(subtoc)) {
      // Map over the array.
      return subtoc.map(function (row) {
        var newRow = {};
        var key = Object.keys(row)[0];

        newRow[key] = sorter(row, paths);
        return newRow;
      });
    } else {
      return subtoc;
    }

  })[0]; // Note that the end result has a list inside of a list.
};
<|endoftext|>