<commit_before>/*!
 * miner.js - inefficient miner for bcoin (because we can)
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var utils = require('../utils/utils');
var co = require('../utils/co');
var assert = require('assert');
var constants = require('../protocol/constants');
var AsyncObject = require('../utils/async');
var MinerBlock = require('./minerblock');
var Address = require('../primitives/address');
var time = require('../net/timedata');

/**
 * A bitcoin miner (supports mining witness blocks).
 * @exports Miner
 * @constructor
 * @param {Object} options
 * @param {Base58Address} options.address - Payout address.
 * @param {String?} [options.coinbaseFlags="mined by bcoin"]
 * @property {Boolean} running
 * @property {MinerBlock} attempt
 * @emits Miner#block
 * @emits Miner#status
 */

function Miner(options) {
  if (!(this instanceof Miner))
    return new Miner(options);

  AsyncObject.call(this);

  assert(options, 'Miner requires options.');
  assert(options.chain, 'Miner requires a blockchain.');

  this.chain = options.chain;
  this.mempool = options.mempool;
  this.network = this.chain.network;
  this.logger = options.logger || this.chain.logger;

  this.running = false;
  this.stopping = false;
  this.attempt = null;
  this.since = 0;

  this.version = -1;
  this.addresses = [];
  this.coinbaseFlags = new Buffer('mined by bcoin', 'ascii');

  this.minWeight = 0;
  this.maxWeight = 750000 * constants.WITNESS_SCALE_FACTOR;
  this.priorityWeight = 50000 * constants.WITNESS_SCALE_FACTOR;
  this.minPriority = constants.tx.FREE_THRESHOLD;

  this._initOptions(options);
  this._init();
}

utils.inherits(Miner, AsyncObject);

/**
 * Initialize the miner options.
 * @private
 */

Miner.prototype._initOptions = function _initOptions(options) {
  var i, flags;

  if (options.version != null) {
    assert(utils.isNumber(options.version));
    this.version = options.version;
  }

  if (options.address)
    this.addAddress(options.address);

  if (options.addresses) {
    assert(Array.isArray(options.addresses));
    for (i = 0; i < options.addresses.length; i++)
      this.addAddress(options.addresses[i]);
  }

  if (options.coinbaseFlags) {
    flags = options.coinbaseFlags;
    if (typeof flags === 'string')
      flags = new Buffer(flags, 'utf8');
    assert(Buffer.isBuffer(flags));
    this.coinbaseFlags = flags;
  }

  if (options.minWeight != null) {
    assert(utils.isNumber(options.minWeight));
    this.minWeight = options.minWeight;
  }

  if (options.maxWeight != null) {
    assert(utils.isNumber(options.maxWeight));
    this.maxWeight = options.maxWeight;
  }

  if (options.priorityWeight != null) {
    assert(utils.isNumber(options.priorityWeight));
    this.priorityWeight = options.priorityWeight;
  }

  if (options.minPriority != null) {
    assert(utils.isNumber(options.minPriority));
    this.minPriority = options.minPriority;
  }
};

/**
 * Initialize the miner.
 * @private
 */

Miner.prototype._init = function _init() {
  var self = this;

  this.chain.on('tip', function(tip) {
    if (!self.attempt)
      return;

    if (self.attempt.block.prevBlock === tip.prevBlock)
      self.attempt.destroy();
  });

  this.on('block', function(block) {
    // Emit the block hex as a failsafe (in case we can't send it)
    self.logger.info('Found block: %d (%s).', block.height, block.rhash);
    self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
  });

  this.on('status', function(stat) {
    self.logger.info(
      'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
      stat.hashrate / 1000 | 0,
      stat.hashes,
      stat.target,
      stat.height,
      stat.best);
  });
};

/**
 * Open the miner, wait for the chain and mempool to load.
 * @alias Miner#open
 * @returns {Promise}
 */

Miner.prototype._open = co(function* open() {
  yield this.chain.open();

  if (this.mempool)
    yield this.mempool.open();

  this.logger.info('Miner loaded (flags=%s).',
    this.coinbaseFlags.toString('utf8'));
});

/**
 * Close the miner.
 * @alias Miner#close
 * @returns {Promise}
 */

Miner.prototype._close = co(function* close() {
  if (!this.running)
    return;

  if (this.stopping) {
    yield this._onStop();
    return;
  }

  yield this.stop();
});

/**
 * Start mining.
 * @param {Number?} version - Custom block version.
 */

Miner.prototype.start = co(function* start() {
  var self = this;
  var block;

  assert(!this.running, 'Miner is already running.');

  this.running = true;
  this.stopping = false;

  for (;;) {
    this.attempt = null;

    try {
      this.attempt = yield this.createBlock();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.attempt.on('status', function(status) {
      self.emit('status', status);
    });

    try {
      block = yield this.attempt.mineAsync();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    if (!block)
      continue;

    try {
      yield this.chain.add(block);
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.emit('block', block);
  }

  this.emit('done');
});

/**
 * Stop mining.
 */

Miner.prototype.stop = co(function* stop() {
  assert(this.running, 'Miner is not running.');
  assert(!this.stopping, 'Miner is already stopping.');

  this.stopping = true;

  if (this.attempt)
    this.attempt.destroy();

  yield this._onDone();

  this.running = false;
  this.stopping = false;
  this.attempt = null;

  this.emit('stop');
});

/**
 * Wait for `done` event.
 */

Miner.prototype._onDone = function _onDone() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('done', resolve);
  });
};

/**
 * Wait for `stop` event.
 */

Miner.prototype._onStop = function _onStop() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('stop', resolve);
  });
};

/**
 * Create a block "attempt".
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns {@link MinerBlock}.
 */

Miner.prototype.createBlock = co(function* createBlock(tip, address) {
  var version = this.version;
  var ts, attempt, target, locktime;

  if (!tip)
    tip = this.chain.tip;

  assert(tip);

  ts = Math.max(time.now(), tip.ts + 1);
  locktime = ts;

  target = yield this.chain.getTargetAsync(ts, tip);

  if (version === -1)
    version = yield this.chain.computeBlockVersion(tip);

  if (this.chain.state.hasMTP())
    locktime = yield tip.getMedianTimeAsync();

  if (!address)
    address = this.getAddress();

  attempt = new MinerBlock({
    tip: tip,
    version: version,
    bits: target,
    locktime: locktime,
    flags: this.chain.state.flags,
    address: address,
    coinbaseFlags: this.coinbaseFlags,
    witness: this.chain.state.hasWitness(),
    network: this.network
  });

  this.fill(attempt);

  return attempt;
});

/**
 * Mine a single block.
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns [{@link Block}].
 */

Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
  var attempt = yield this.createBlock(tip, address);
  return yield attempt.mineAsync();
});

/**
 * Notify the miner that a new tx has entered the mempool.
 * @param {MempoolEntry} entry
 */

Miner.prototype.notifyEntry = function notifyEntry() {
  if (!this.running)
    return;

  if (!this.attempt)
    return;

  if (++this.since > 20) {
    this.since = 0;
    this.attempt.destroy();
  }
};

/**
 * Add an address to the address list.
 * @param {Address} address
 */

Miner.prototype.addAddress = function addAddress(address) {
  this.addresses.push(Address(address));
};

/**
 * Get a random address from the address list.
 * @returns {Address}
 */

Miner.prototype.getAddress = function getAddress() {
  assert(this.addresses.length !== 0, 'No address passed in for miner.');
  return this.addresses[Math.random() * this.addresses.length | 0];
};

/**
 * Get mempool entries, sort by dependency order.
 * @returns {MempoolEntry[]}
 */

Miner.prototype.fill = function fill(attempt) {
  var depMap = {};
  var block = attempt.block;
  var queue = new Queue(cmpPriority);
  var priority = true;
  var i, j, entry, item, tx, hash, input;
  var prev, deps, hashes, weight, sigops;

  if (!this.mempool)
    return [];

  hashes = this.mempool.getSnapshot();

  for (i = 0; i < hashes.length; i++) {
    hash = hashes[i];
    entry = this.mempool.getEntry(hash);
    item = new QueueItem(entry, attempt);
    tx = item.tx;

    if (tx.isCoinbase())
      throw new Error('Cannot add coinbase to block.');

    if (!tx.hasCoins())
      throw new Error('Cannot add empty tx to block.');

    if (!tx.isFinal(attempt.height, attempt.locktime))
      continue;

    for (j = 0; j < tx.inputs.length; j++) {
      input = tx.inputs[j];
      prev = input.prevout.hash;

      if (!this.mempool.hasTX(prev))
        continue;

      item.depCount += 1;

      if (!depMap[prev])
        depMap[prev] = [];

      depMap[prev].push(item);
    }

    if (item.depCount > 0)
      continue;

    queue.push(item);
  }

  while (queue.size() > 0) {
    item = queue.pop();
    tx = item.tx;
    hash = item.hash;
    weight = attempt.weight;
    sigops = attempt.sigops;

    if (!attempt.witness && tx.hasWitness())
      continue;

    weight += tx.getWeight();

    if (weight > this.maxWeight)
      continue;

    sigops += tx.getSigopsWeight(attempt.flags);

    if (sigops > constants.block.MAX_SIGOPS_WEIGHT)
      continue;

    if (priority) {
      if (weight > this.priorityWeight || item.priority < this.minPriority) {
        queue.cmp = cmpRate;
        priority = false;
        queue.push(item);
        continue;
      }
    } else {
      if (item.free && weight >= this.minWeight)
        continue;
    }

    attempt.weight = weight;
    attempt.sigops = sigops;
    attempt.fees += item.fee;

    block.txs.push(tx.clone());

    deps = depMap[hash];

    if (!deps)
      continue;

    for (j = 0; j < deps.length; j++) {
      item = deps[j];
      if (--item.depCount === 0)
        queue.push(item);
    }
  }

  attempt.updateCoinbase();
  attempt.updateMerkle();

  assert(block.getWeight() <= attempt.weight);
};

/**
 * QueueItem
 * @constructor
 */

function QueueItem(entry, attempt) {
  this.tx = entry.tx;
  this.hash = entry.tx.hash('hex');
  this.fee = entry.getFee();
  this.rate = entry.getRate();
  this.priority = entry.getPriority(attempt.height);
  this.free = entry.isFree(attempt.height);
  this.depCount = 0;
}

/**
 * Queue
 * @constructor
 */

function Queue(cmp) {
  this.cmp = cmp;
  this.items = [];
}

Queue.prototype.size = function size() {
  return this.items.length;
};

Queue.prototype.push = function push(item) {
  utils.binaryInsert(this.items, item, this.cmp);
};

Queue.prototype.pop = function pop() {
  return this.items.pop();
};

/*
 * Helpers
 */

function cmpPriority(a, b) {
  return a.priority - b.priority;
}

function cmpRate(a, b) {
  return a.rate - b.rate;
}

/*
 * Expose
 */

module.exports = Miner;
<commit_msg>miner: rename fill. add comments.<commit_after>/*!
 * miner.js - inefficient miner for bcoin (because we can)
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var utils = require('../utils/utils');
var co = require('../utils/co');
var assert = require('assert');
var constants = require('../protocol/constants');
var AsyncObject = require('../utils/async');
var MinerBlock = require('./minerblock');
var Address = require('../primitives/address');
var time = require('../net/timedata');

/**
 * A bitcoin miner (supports mining witness blocks).
 * @exports Miner
 * @constructor
 * @param {Object} options
 * @param {Base58Address} options.address - Payout address.
 * @param {String?} [options.coinbaseFlags="mined by bcoin"]
 * @property {Boolean} running
 * @property {MinerBlock} attempt
 * @emits Miner#block
 * @emits Miner#status
 */

function Miner(options) {
  if (!(this instanceof Miner))
    return new Miner(options);

  AsyncObject.call(this);

  assert(options, 'Miner requires options.');
  assert(options.chain, 'Miner requires a blockchain.');

  this.chain = options.chain;
  this.mempool = options.mempool;
  this.network = this.chain.network;
  this.logger = options.logger || this.chain.logger;

  this.running = false;
  this.stopping = false;
  this.attempt = null;
  this.since = 0;

  this.version = -1;
  this.addresses = [];
  this.coinbaseFlags = new Buffer('mined by bcoin', 'ascii');

  this.minWeight = 0;
  this.maxWeight = 750000 * constants.WITNESS_SCALE_FACTOR;
  this.maxSigops = constants.block.MAX_SIGOPS_WEIGHT;
  this.priorityWeight = 50000 * constants.WITNESS_SCALE_FACTOR;
  this.minPriority = constants.tx.FREE_THRESHOLD;

  this._initOptions(options);
  this._init();
}

utils.inherits(Miner, AsyncObject);

/**
 * Initialize the miner options.
 * @private
 */

Miner.prototype._initOptions = function _initOptions(options) {
  var i, flags;

  if (options.version != null) {
    assert(utils.isNumber(options.version));
    this.version = options.version;
  }

  if (options.address)
    this.addAddress(options.address);

  if (options.addresses) {
    assert(Array.isArray(options.addresses));
    for (i = 0; i < options.addresses.length; i++)
      this.addAddress(options.addresses[i]);
  }

  if (options.coinbaseFlags) {
    flags = options.coinbaseFlags;
    if (typeof flags === 'string')
      flags = new Buffer(flags, 'utf8');
    assert(Buffer.isBuffer(flags));
    this.coinbaseFlags = flags;
  }

  if (options.minWeight != null) {
    assert(utils.isNumber(options.minWeight));
    this.minWeight = options.minWeight;
  }

  if (options.maxWeight != null) {
    assert(utils.isNumber(options.maxWeight));
    this.maxWeight = options.maxWeight;
  }

  if (options.maxSigops != null) {
    assert(utils.isNumber(options.maxSigops));
    assert(options.maxSigops <= constants.block.MAX_SIGOPS_WEIGHT);
    this.maxSigops = options.maxSigops;
  }

  if (options.priorityWeight != null) {
    assert(utils.isNumber(options.priorityWeight));
    this.priorityWeight = options.priorityWeight;
  }

  if (options.minPriority != null) {
    assert(utils.isNumber(options.minPriority));
    this.minPriority = options.minPriority;
  }
};

/**
 * Initialize the miner.
 * @private
 */

Miner.prototype._init = function _init() {
  var self = this;

  this.chain.on('tip', function(tip) {
    if (!self.attempt)
      return;

    if (self.attempt.block.prevBlock === tip.prevBlock)
      self.attempt.destroy();
  });

  this.on('block', function(block) {
    // Emit the block hex as a failsafe (in case we can't send it)
    self.logger.info('Found block: %d (%s).', block.height, block.rhash);
    self.logger.debug('Raw: %s', block.toRaw().toString('hex'));
  });

  this.on('status', function(stat) {
    self.logger.info(
      'Miner: hashrate=%dkhs hashes=%d target=%d height=%d best=%s',
      stat.hashrate / 1000 | 0,
      stat.hashes,
      stat.target,
      stat.height,
      stat.best);
  });
};

/**
 * Open the miner, wait for the chain and mempool to load.
 * @alias Miner#open
 * @returns {Promise}
 */

Miner.prototype._open = co(function* open() {
  yield this.chain.open();

  if (this.mempool)
    yield this.mempool.open();

  this.logger.info('Miner loaded (flags=%s).',
    this.coinbaseFlags.toString('utf8'));
});

/**
 * Close the miner.
 * @alias Miner#close
 * @returns {Promise}
 */

Miner.prototype._close = co(function* close() {
  if (!this.running)
    return;

  if (this.stopping) {
    yield this._onStop();
    return;
  }

  yield this.stop();
});

/**
 * Start mining.
 * @param {Number?} version - Custom block version.
 */

Miner.prototype.start = co(function* start() {
  var self = this;
  var block;

  assert(!this.running, 'Miner is already running.');

  this.running = true;
  this.stopping = false;

  for (;;) {
    this.attempt = null;

    try {
      this.attempt = yield this.createBlock();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.attempt.on('status', function(status) {
      self.emit('status', status);
    });

    try {
      block = yield this.attempt.mineAsync();
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    if (!block)
      continue;

    try {
      yield this.chain.add(block);
    } catch (e) {
      if (this.stopping)
        break;
      this.emit('error', e);
      continue;
    }

    if (this.stopping)
      break;

    this.emit('block', block);
  }

  this.emit('done');
});

/**
 * Stop mining.
 */

Miner.prototype.stop = co(function* stop() {
  assert(this.running, 'Miner is not running.');
  assert(!this.stopping, 'Miner is already stopping.');

  this.stopping = true;

  if (this.attempt)
    this.attempt.destroy();

  yield this._onDone();

  this.running = false;
  this.stopping = false;
  this.attempt = null;

  this.emit('stop');
});

/**
 * Wait for `done` event.
 */

Miner.prototype._onDone = function _onDone() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('done', resolve);
  });
};

/**
 * Wait for `stop` event.
 */

Miner.prototype._onStop = function _onStop() {
  var self = this;
  return new Promise(function(resolve, reject) {
    self.once('stop', resolve);
  });
};

/**
 * Create a block "attempt".
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns {@link MinerBlock}.
 */

Miner.prototype.createBlock = co(function* createBlock(tip, address) {
  var version = this.version;
  var ts, attempt, target, locktime;

  if (!tip)
    tip = this.chain.tip;

  assert(tip);

  ts = Math.max(time.now(), tip.ts + 1);
  locktime = ts;

  target = yield this.chain.getTargetAsync(ts, tip);

  if (version === -1)
    version = yield this.chain.computeBlockVersion(tip);

  if (this.chain.state.hasMTP())
    locktime = yield tip.getMedianTimeAsync();

  if (!address)
    address = this.getAddress();

  attempt = new MinerBlock({
    tip: tip,
    version: version,
    bits: target,
    locktime: locktime,
    flags: this.chain.state.flags,
    address: address,
    coinbaseFlags: this.coinbaseFlags,
    witness: this.chain.state.hasWitness(),
    network: this.network
  });

  this.build(attempt);

  return attempt;
});

/**
 * Mine a single block.
 * @param {ChainEntry} tip
 * @returns {Promise} - Returns [{@link Block}].
 */

Miner.prototype.mineBlock = co(function* mineBlock(tip, address) {
  var attempt = yield this.createBlock(tip, address);
  return yield attempt.mineAsync();
});

/**
 * Notify the miner that a new tx has entered the mempool.
 * @param {MempoolEntry} entry
 */

Miner.prototype.notifyEntry = function notifyEntry() {
  if (!this.running)
    return;

  if (!this.attempt)
    return;

  if (++this.since > 20) {
    this.since = 0;
    this.attempt.destroy();
  }
};

/**
 * Add an address to the address list.
 * @param {Address} address
 */

Miner.prototype.addAddress = function addAddress(address) {
  this.addresses.push(Address(address));
};

/**
 * Get a random address from the address list.
 * @returns {Address}
 */

Miner.prototype.getAddress = function getAddress() {
  assert(this.addresses.length !== 0, 'No address passed in for miner.');
  return this.addresses[Math.random() * this.addresses.length | 0];
};

/**
 * Get mempool entries, sort by dependency order.
 * Prioritize by priority and fee rates.
 * @returns {MempoolEntry[]}
 */

Miner.prototype.build = function build(attempt) {
  var depMap = {};
  var block = attempt.block;
  var queue = new Queue(cmpPriority);
  var priority = true;
  var i, j, entry, item, tx, hash, input;
  var prev, deps, hashes, weight, sigops;

  if (!this.mempool)
    return [];

  hashes = this.mempool.getSnapshot();

  for (i = 0; i < hashes.length; i++) {
    hash = hashes[i];
    entry = this.mempool.getEntry(hash);
    item = new QueueItem(entry, attempt);
    tx = item.tx;

    if (tx.isCoinbase())
      throw new Error('Cannot add coinbase to block.');

    if (!tx.hasCoins())
      throw new Error('Cannot add empty tx to block.');

    if (!tx.isFinal(attempt.height, attempt.locktime))
      continue;

    for (j = 0; j < tx.inputs.length; j++) {
      input = tx.inputs[j];
      prev = input.prevout.hash;

      if (!this.mempool.hasTX(prev))
        continue;

      item.depCount += 1;

      if (!depMap[prev])
        depMap[prev] = [];

      depMap[prev].push(item);
    }

    if (item.depCount > 0)
      continue;

    queue.push(item);
  }

  while (queue.size() > 0) {
    item = queue.pop();
    tx = item.tx;
    hash = item.hash;
    weight = attempt.weight;
    sigops = attempt.sigops;

    if (!attempt.witness && tx.hasWitness())
      continue;

    weight += tx.getWeight();

    if (weight > this.maxWeight)
      continue;

    sigops += tx.getSigopsWeight(attempt.flags);

    if (sigops > this.maxSigops)
      continue;

    if (priority) {
      if (weight > this.priorityWeight || item.priority < this.minPriority) {
        // Todo: Compare descendant rate with
        // cumulative fees and cumulative vsize.
        queue.cmp = cmpRate;
        priority = false;
        queue.push(item);
        continue;
      }
    } else {
      if (item.free && weight >= this.minWeight)
        continue;
    }

    attempt.weight = weight;
    attempt.sigops = sigops;
    attempt.fees += item.fee;

    block.txs.push(tx.clone());

    deps = depMap[hash];

    if (!deps)
      continue;

    for (j = 0; j < deps.length; j++) {
      item = deps[j];
      if (--item.depCount === 0)
        queue.push(item);
    }
  }

  attempt.updateCoinbase();
  attempt.updateMerkle();

  assert(block.getWeight() <= attempt.weight);
};

/**
 * QueueItem
 * @constructor
 */

function QueueItem(entry, attempt) {
  this.tx = entry.tx;
  this.hash = entry.tx.hash('hex');
  this.fee = entry.getFee();
  this.rate = entry.getRate();
  this.priority = entry.getPriority(attempt.height);
  this.free = entry.isFree(attempt.height);
  this.depCount = 0;
}

/**
 * Queue
 * @constructor
 */

function Queue(cmp) {
  this.cmp = cmp;
  this.items = [];
}

Queue.prototype.size = function size() {
  return this.items.length;
};

Queue.prototype.push = function push(item) {
  utils.binaryInsert(this.items, item, this.cmp);
};

Queue.prototype.pop = function pop() {
  return this.items.pop();
};

/*
 * Helpers
 */

function cmpPriority(a, b) {
  return a.priority - b.priority;
}

function cmpRate(a, b) {
  return a.rate - b.rate;
}

/*
 * Expose
 */

module.exports = Miner;
<|endoftext|>


<commit_before>
'use strict';

let co = require('co'),
    redisModule = require('../../../server/lib/redis'),
    redis = redisModule.createClient();

module.exports = {
    local: {
        dontReportSauceLabs: true
    },

    before: function(done) {
        co(function*() {
            // Clear redis database
            yield redis.flushdb();
            yield redis.quit();

            yield redisModule.initDB();
            done();
        })();
    }
};
<commit_msg>Make sure test run exits no matter what<commit_after>
'use strict';

let co = require('co'),
    redisModule = require('../../../server/lib/redis'),
    redis = redisModule.createClient();

module.exports = {
    local: {
        dontReportSauceLabs: true
    },

    before: function(done) {
        co(function*() {
            // Clear redis database
            yield redis.flushdb();
            yield redis.quit();

            yield redisModule.initDB();
            done();
        })();
    },

    after: function(done) {
        setTimeout(function() {
            // Some of the MAS server libs don't exit cleanly because of Redis connections
            process.exit(0);
        }, 1000);

        done();
    }
};
<|endoftext|>


<commit_before>
(function(Factlink) {

    // Indicator object which will manage the indicator which shows some basic info
// of each Factlink
Factlink.Indicator = (function() {
    // By default the element object is undefined
    var el,
        // Is the indicator currently visible?
        visible = false,
        // This object will store the timeout id
        timeout,
        // Current shown Factlink
        currentId,
        // Position of the indication
        x, y;

    return {
        // Makes the indicator show for the Factlink with id ID
        showFor: function( id, x_in, y_in) {
            // Get the Factlink-object
            var fl = $( 'span.factlink[data-factid=' + id + ']');

            believe_percentage = fl.first().data('fact-believe-percentage');
            disbelieve_percentage = fl.first().data('fact-disbelieve-percentage');
            doubt_percentage = fl.first().data('fact-doubt-percentage');
            authority = fl.first().data('fact-authority');
            // On creation of a new Fact, authority is undefined. Fall to default
            if ( authority === 'undefined' ) {
              authority = 0;
            }

            prevalent = 'doubt';
            prevalent_percentage = doubt_percentage;

            if (disbelieve_percentage > prevalent_percentage){
                prevalent = 'disbelieve';
                prevalent_percentage = disbelieve_percentage;
            }
            if (believe_percentage > prevalent_percentage){
                prevalent = 'believe';
                prevalent_percentage = believe_percentage;
            }

            if ( id !== currentId ) {
                x = x_in + 10;
                y = y_in - el.outerHeight(true) - 10;
            }

            window.clearTimeout( timeout );

            // Store the currentId;
            currentId = id;

            // only show indicator if the user at least has the patience to hover for 10 milliseconds
            timeout = window.setTimeout(function(){
                if ( el === undefined ) {
                    return;
                } else {
                  el.find('div.believe, div.disbelieve, div.doubt').hide();
                  el.find('div.'+prevalent).show();
                  el.find('span.authority').html(authority);
                  el.css({ top: y, left: x }).fadeIn(100);
               }
            }, 10);
        },

        hide: function() {
            // Make sure there isn't an timeout running
            window.clearTimeout( timeout );

            // Put the hiding in a timeout, so it can be stopped when a user
            // hovers the indication
            timeout = window.setTimeout(function(){
                el.fadeOut(100);
                currentId = undefined;
            }, 500);
        },

        // Set the element object when it's set.
        // Typically this will only be called from the initial ajax-call made
        // in init.js
        setElement: function( newEl ) {
            var self = this;

            el = newEl;

            el  .bind('mouseenter', function() {
                    window.clearTimeout(timeout);

                    // Keep the Factlink highlighted
                    $( '[data-factid=' + currentId + ']' )
                        .addClass('fl-active');
                })
                .bind('mouseleave', function() {
                    self.hide();

                    // Stop hightlighting the Faclink
                    $( '[data-factid=' + currentId + ']' )
                        .removeClass('fl-active');
                })
                .bind('click', function() {
                    self.hide();
                    console.info("currentid = "+currentId);
                    $( 'span.factlink[data-factid=' + currentId + ']:first').click();
                    return false;
                });
        },
    };
})();
$.ajax({
  method: 'get',
  dataType: 'jsonp',
  crossDomain: true,
  url: window.location.protocol + '//' + FactlinkConfig.api + '/factlink/indication.js',
  success: function( data ) {
    Factlink.Indicator.setElement( $( data ).attr('id','factlink-indicator').appendTo('body') );
  }
});

var showIndicator = function(e,factId,orig_e) {
            Factlink.Indicator.showFor(factId, orig_e.pageX - 10, $(orig_e.target).offset().top + 10);
};

var stopShowingIndicator = function(e,factId,orig_e) {
    Factlink.Indicator.hide();
};

$(window).bind("factlink:factHighlighted", showIndicator);
$(window).bind("factlink:factUnhighlighted",stopShowingIndicator);

})(window.Factlink);
<commit_msg>timeout<commit_after>
(function(Factlink) {

    // Indicator object which will manage the indicator which shows some basic info
// of each Factlink
Factlink.Indicator = (function() {
    // By default the element object is undefined
    var el,
        // Is the indicator currently visible?
        visible = false,
        // This object will store the timeout id
        timeout,
        // Current shown Factlink
        currentId,
        // Position of the indication
        x, y;

    return {
        // Makes the indicator show for the Factlink with id ID
        showFor: function( id, x_in, y_in) {
            // Get the Factlink-object
            var fl = $( 'span.factlink[data-factid=' + id + ']');

            believe_percentage = fl.first().data('fact-believe-percentage');
            disbelieve_percentage = fl.first().data('fact-disbelieve-percentage');
            doubt_percentage = fl.first().data('fact-doubt-percentage');
            authority = fl.first().data('fact-authority');
            // On creation of a new Fact, authority is undefined. Fall to default
            if ( authority === 'undefined' ) {
              authority = 0;
            }

            prevalent = 'doubt';
            prevalent_percentage = doubt_percentage;

            if (disbelieve_percentage > prevalent_percentage){
                prevalent = 'disbelieve';
                prevalent_percentage = disbelieve_percentage;
            }
            if (believe_percentage > prevalent_percentage){
                prevalent = 'believe';
                prevalent_percentage = believe_percentage;
            }

            if ( id !== currentId ) {
                x = x_in + 10;
                y = y_in - el.outerHeight(true) - 10;
            }

            window.clearTimeout( timeout );

            // Store the currentId;
            currentId = id;

            // only show indicator if the user at least has the patience to hover for 10 milliseconds
            timeout = window.setTimeout(function(){
                if ( el === undefined ) {
                    return;
                } else {
                  el.find('div.believe, div.disbelieve, div.doubt').hide();
                  el.find('div.'+prevalent).show();
                  el.find('span.authority').html(authority);
                  el.css({ top: y, left: x }).fadeIn(100);
               }
            }, 500);
        },

        hide: function() {
            // Make sure there isn't an timeout running
            window.clearTimeout( timeout );

            // Put the hiding in a timeout, so it can be stopped when a user
            // hovers the indication
            timeout = window.setTimeout(function(){
                el.fadeOut(100);
                currentId = undefined;
            }, 500);
        },

        // Set the element object when it's set.
        // Typically this will only be called from the initial ajax-call made
        // in init.js
        setElement: function( newEl ) {
            var self = this;

            el = newEl;

            el  .bind('mouseenter', function() {
                    window.clearTimeout(timeout);

                    // Keep the Factlink highlighted
                    $( '[data-factid=' + currentId + ']' )
                        .addClass('fl-active');
                })
                .bind('mouseleave', function() {
                    self.hide();

                    // Stop hightlighting the Faclink
                    $( '[data-factid=' + currentId + ']' )
                        .removeClass('fl-active');
                })
                .bind('click', function() {
                    self.hide();
                    console.info("currentid = "+currentId);
                    $( 'span.factlink[data-factid=' + currentId + ']:first').click();
                    return false;
                });
        },
    };
})();
$.ajax({
  method: 'get',
  dataType: 'jsonp',
  crossDomain: true,
  url: window.location.protocol + '//' + FactlinkConfig.api + '/factlink/indication.js',
  success: function( data ) {
    Factlink.Indicator.setElement( $( data ).attr('id','factlink-indicator').appendTo('body') );
  }
});

var showIndicator = function(e,factId,orig_e) {
            Factlink.Indicator.showFor(factId, orig_e.pageX - 10, $(orig_e.target).offset().top + 10);
};

var stopShowingIndicator = function(e,factId,orig_e) {
    Factlink.Indicator.hide();
};

$(window).bind("factlink:factHighlighted", showIndicator);
$(window).bind("factlink:factUnhighlighted",stopShowingIndicator);

})(window.Factlink);
<|endoftext|>


<commit_before>var spawn = require("child_process").spawn
  , Path = require("path")
  , Url = require("url")
  , fs = require("fs")
  , extend = require("./util").extend
  , util = require("util")

  , CGI_NULL = ""
  , EOL = /\r?\n/
  , EMPTY_LINE = /(?:\r?\n){2}/
  , defaultBin = {
      pl: "perl"
    , php: "php-cgi"
    }

exports.handler = cgiHandler
exports.preset = Object.keys(defaultBin).reduce(function(preset, ext) {
  preset[ext] = function(conf) {
    conf = extend({
      ext: ext
    , bin: defaultBin[ext]
    }, conf)
    return cgiHandler(conf)
  }
  return preset
}, {})

function cgiHandler(conf) {
  conf = conf || {}

  var root = conf.root || process.cwd()
    , ext = conf.ext
  if (!ext) {
    throw new Error("Must specify a file extension for CGI middleware")
  }

  var extMatch = new RegExp("\." + ext + "$")
    , cgiBin = conf.bin
    , indexScript = "index." + ext
    , sharedMeta = {
        GATEWAY_INTERFACE: "CGI/1.1"
      , REDIRECT_STATUS: "200"
      }

  return function(req, res, next) {
    var url = Url.parse(req.url)
      , pathname = url.pathname
      , scriptName
    if (pathname.charAt(pathname.length - 1) == "/") {
      pathname += indexScript
    }

    if (!extMatch.test(pathname)) return next()

    scriptName = Path.join(root, pathname)

    fs.stat(scriptName, function(err, stat) {
      if (err || !stat.isFile()) return next()

      var finalMeta = {}
        , app

      extend(finalMeta, sharedMeta)
      extend(finalMeta, {
        SCRIPT_NAME: pathname
      , QUERY_STRING: url.query || CGI_NULL
      , SCRIPT_FILENAME: scriptName
      , DOCUMENT_ROOT: root
      , REQUEST_METHOD: req.method
      , REMOTE_ADDRESS: req.connection.remoteAddress
      , SERVER_PROTOCOL: "HTTP/" + req.httpVersion
      , REQUEST_URI: req.url
      })
      extend(finalMeta, headerToMeta(req.headers))

      app = cgiBin
        ? spawn(cgiBin, [scriptName], finalMeta)
        : spawn(scriptName, [], finalMeta)

      cgiPump(app.stdout, res)
    })
  }
}

function cgiPump(readStream, res) {
  var headerDone = false
    , trailing = ""

  readStream.on("end", function() {
    res.end()
  })

  readStream.on("data", function(chunk) {
    if (headerDone) return res.write(chunk)

    trailing += chunk.toString("ascii")
    headerDone = EMPTY_LINE.test(trailing)
    if (!headerDone) return

    var firstBody = RegExp.rightContext
      , rawHeaders = RegExp.leftContext.split(EOL)
      , headers = {}

    rawHeaders.reduce(function(headers, line) {
      var matched = line.match(/^([^:]+): +(.*)$/)
      if (matched) headers[matched[1]] = matched[2]
      return headers
    }, headers)

    var status = parseInt(headers.Status, 10) || 200
    delete headers.Status
    res.writeHead(status, headers)
    res.write(firstBody)
  })
}

function headerToMeta(headers) {
  var meta = {}
    , metaKey, key
  for (key in headers) {
    metaKey = "HTTP_" + key.replace(/-/g, "_").toUpperCase()
    meta[metaKey] = headers[key]
  }
  return meta
}
<commit_msg>remotePort for CGI meta var<commit_after>var spawn = require("child_process").spawn
  , Path = require("path")
  , Url = require("url")
  , fs = require("fs")
  , extend = require("./util").extend
  , util = require("util")

  , CGI_NULL = ""
  , EOL = /\r?\n/
  , EMPTY_LINE = /(?:\r?\n){2}/
  , defaultBin = {
      pl: "perl"
    , php: "php-cgi"
    }

exports.handler = cgiHandler
exports.preset = Object.keys(defaultBin).reduce(function(preset, ext) {
  preset[ext] = function(conf) {
    conf = extend({
      ext: ext
    , bin: defaultBin[ext]
    }, conf)
    return cgiHandler(conf)
  }
  return preset
}, {})

function cgiHandler(conf) {
  conf = conf || {}

  var root = conf.root || process.cwd()
    , ext = conf.ext
  if (!ext) {
    throw new Error("Must specify a file extension for CGI middleware")
  }

  var extMatch = new RegExp("\." + ext + "$")
    , cgiBin = conf.bin
    , indexScript = "index." + ext
    , sharedMeta = {
        GATEWAY_INTERFACE: "CGI/1.1"
      , REDIRECT_STATUS: "200"
      }

  return function(req, res, next) {
    var url = Url.parse(req.url)
      , pathname = url.pathname
      , scriptName
    if (pathname.charAt(pathname.length - 1) == "/") {
      pathname += indexScript
    }

    if (!extMatch.test(pathname)) return next()

    scriptName = Path.join(root, pathname)

    fs.stat(scriptName, function(err, stat) {
      if (err || !stat.isFile()) return next()

      var finalMeta = {}
        , app

      extend(finalMeta, sharedMeta)
      extend(finalMeta, {
        SCRIPT_NAME: pathname
      , QUERY_STRING: url.query || CGI_NULL
      , SCRIPT_FILENAME: scriptName
      , DOCUMENT_ROOT: root
      , REQUEST_METHOD: req.method
      , REMOTE_ADDRESS: req.connection.remoteAddress
      , REMOTE_PORT: req.connection.remotePort
      , SERVER_PROTOCOL: "HTTP/" + req.httpVersion
      , REQUEST_URI: req.url
      })
      extend(finalMeta, headerToMeta(req.headers))

      app = cgiBin
        ? spawn(cgiBin, [scriptName], finalMeta)
        : spawn(scriptName, [], finalMeta)

      cgiPump(app.stdout, res)
    })
  }
}

function cgiPump(readStream, res) {
  var headerDone = false
    , trailing = ""

  readStream.on("end", function() {
    res.end()
  })

  readStream.on("data", function(chunk) {
    if (headerDone) return res.write(chunk)

    trailing += chunk.toString("ascii")
    headerDone = EMPTY_LINE.test(trailing)
    if (!headerDone) return

    var firstBody = RegExp.rightContext
      , rawHeaders = RegExp.leftContext.split(EOL)
      , headers = {}

    rawHeaders.reduce(function(headers, line) {
      var matched = line.match(/^([^:]+): +(.*)$/)
      if (matched) headers[matched[1]] = matched[2]
      return headers
    }, headers)

    var status = parseInt(headers.Status, 10) || 200
    delete headers.Status
    res.writeHead(status, headers)
    res.write(firstBody)
  })
}

function headerToMeta(headers) {
  var meta = {}
    , metaKey, key
  for (key in headers) {
    metaKey = "HTTP_" + key.replace(/-/g, "_").toUpperCase()
    meta[metaKey] = headers[key]
  }
  return meta
}
<|endoftext|>


<commit_before>var Rule = require('./rule').Rule;

/**
 * Given an element, generates a function that checks that that element is not in the dom.
 */
var createGenericCallback = function(element){
  return function(dom){
    'use strict';

    dom.$(element).each(function(index, item){
      throw dom.$(item).parent().html();
    });
  };
};

/**
 * Creates an options json that stores the options for a generic rule.
 */
var createElementRule = function(options){
  return {
    element: options.element,
    name:    'Do not use ' + options.element + ' element',
    message: 'Please remove the ' + options.element + ' element',
    ruleUrl:  options.ruleUrl,
    callback: createGenericCallback(options.element)
  };
};

/**
 * RuleFactory object.
 * Decouples rule creation from ArialLinter
 */
function RuleFactory() {
  'use strict';
  this.rulesMap = {};
}

/**
 * Make method to create rule objects
 * @param  {string} type String specifying the rule type
 * @return {object}      a rule object
 */
RuleFactory.prototype.makeRule = function(type) {
  'use strict';

  //If rule is not already in the map
  //then create it and store it
  if (!this.rulesMap.hasOwnProperty(type)) {
    var genericRule;

    switch(type){
      case 'uelement':
        genericRule = new Rule(createElementRule({
          element: 'u',
          ruleUrl: 'http://oaa-accessibility.org/rule/71/'
        }));

        this.rulesMap[type] = genericRule;
      break;

      case 'ielement':
        genericRule = new Rule(createElementRule({
          element: 'i',
          ruleUrl: 'http://oaa-accessibility.org/rule/70/'
        }));

        this.rulesMap[type] = genericRule;
      break;

      case 'fontelement':
        genericRule = new Rule(createElementRule({
          element: 'font',
          ruleUrl: 'http://oaa-accessibility.org/rule/67/'
        }));

        this.rulesMap[type] = genericRule;
      break;

      case 'blinkelement':
        genericRule = new Rule(createElementRule({
          element: 'blink',
          ruleUrl: 'http://oaa-accessibility.org/rule/68/'
        }));

        this.rulesMap[type] = genericRule;
      break;

      case 'marqueeelement':
        genericRule = new Rule(createElementRule({
          element: 'marquee',
          ruleUrl: 'http://oaa-accessibility.org/rule/68/'
        }));

        this.rulesMap[type] = genericRule;
      break;

      case 'belement':
        genericRule = new Rule(createElementRule({
          element: 'b',
          ruleUrl: 'http://oaa-accessibility.org/rule/69/'
        }));

        this.rulesMap[type] = genericRule;
      break;

    default:
      this.rulesMap[type] = require('./rules/' + type);
      break;
    }
  }

  return this.rulesMap[type];
};

module.exports = new RuleFactory();<commit_msg>added a couple of usestrict missing. aligned case statement with switch<commit_after>var Rule = require('./rule').Rule;

/**
 * RuleFactory object.
 * Decouples rule creation from ArialLinter
 */

/**
 * Given an element, generates a function that checks that that element is not in the dom.
 */
var createGenericCallback = function(element) {
  'use strict';
  return function(dom) {
    dom.$(element).each(function(index, item){
      throw dom.$(item).parent().html();
    });
  };
};

/**
 * Creates an options json that stores the options for a generic rule.
 */
var createElementRule = function(options) {
  'use strict';
  return {
    element: options.element,
    name:    'Do not use ' + options.element + ' element',
    message: 'Please remove the ' + options.element + ' element',
    ruleUrl:  options.ruleUrl,
    callback: createGenericCallback(options.element)
  };
};

/**
 * RuleFactory function constructor
 * @constructor
 */
function RuleFactory() {
  'use strict';
  this.rulesMap = {};
}

/**
 * Make method to create rule objects
 * @param  {string} type String specifying the rule type
 * @return {object}      a rule object
 */
RuleFactory.prototype.makeRule = function(type) {
  'use strict';

  //If rule is not already in the map
  //then create it and store it
  if (!this.rulesMap.hasOwnProperty(type)) {
    var genericRule;

    switch(type){
    case 'uelement':
      genericRule = new Rule(createElementRule({
        element: 'u',
        ruleUrl: 'http://oaa-accessibility.org/rule/71/'
      }));

      this.rulesMap[type] = genericRule;
      break;

    case 'ielement':
      genericRule = new Rule(createElementRule({
        element: 'i',
        ruleUrl: 'http://oaa-accessibility.org/rule/70/'
      }));

      this.rulesMap[type] = genericRule;
      break;

    case 'fontelement':
      genericRule = new Rule(createElementRule({
        element: 'font',
        ruleUrl: 'http://oaa-accessibility.org/rule/67/'
      }));

      this.rulesMap[type] = genericRule;
      break;

    case 'blinkelement':
      genericRule = new Rule(createElementRule({
        element: 'blink',
        ruleUrl: 'http://oaa-accessibility.org/rule/68/'
      }));

      this.rulesMap[type] = genericRule;
      break;

    case 'marqueeelement':
      genericRule = new Rule(createElementRule({
        element: 'marquee',
        ruleUrl: 'http://oaa-accessibility.org/rule/68/'
      }));

      this.rulesMap[type] = genericRule;
      break;

    case 'belement':
      genericRule = new Rule(createElementRule({
        element: 'b',
        ruleUrl: 'http://oaa-accessibility.org/rule/69/'
      }));

      this.rulesMap[type] = genericRule;
      break;

    default:
      this.rulesMap[type] = require('./rules/' + type);
      break;
    }
  }

  return this.rulesMap[type];
};

module.exports = new RuleFactory();<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

function parseArgs(argv) {
  let loaders;
  if (argv.loader) {
    if (Array.isArray(argv.loader) && argv.loader.length > 0) {
      loaders = argv.loader;
    } else {
      loaders = [argv.loader];
    }
    loaders = loaders.map(require);
  }
  let command, plugin;
  if (argv.plugin) {
    plugin = require(argv.command);
  } else {
    command = argv.command;
  }
  const spawnOptions = {
    shell: argv.shell
  };
  return {
    command: {
      command,
      plugin,
      args: [...(argv.args || []), ...(argv['--'] || [])]
    },
    configName: argv.name,
    options: {
      configDirName: argv.dirName,
      configFileName: argv.fileName,
      configNameSeparator: argv.separator,
      loaders,
      spawnOptions,
      workingDir: argv.workingDir
    },
    optionsPath: argv.optionsPath
  };
}

function resolveOptionsPath(customPath, optionsFile) {
  const path = require('path');

  return customPath
    ? (customPath.endsWith('.js')
      ? customPath
      : path.resolve(customPath, optionsFile))
    : path.resolve(process.cwd(), optionsFile);
}

function tryOptionsWithPath(path, throwError = false) {
  if (path) {
    try {
      return require(path);
    } catch (e) {
      if (e.code !== 'MODULE_NOT_FOUND' || throwError) {
        throw e;
      }
    }
  }
  return {};
}

function tryOptions(customPath, optionsFile, throwError = false) {
  return tryOptionsWithPath(resolveOptionsPath(customPath, optionsFile), throwError);
}

function loadOptions(argv) {
  const {options: argvOptions, optionsPath, ...rest} = parseArgs(argv);
  return {
    options: require('deepmerge').all([
      {
        spawnOptions: {
          stdio: 'inherit'
        }
      },
      tryOptionsWithPath(optionsPath, true),
      argvOptions,
      tryOptions(process.env.FLAVORS_OPTIONS_PATH, module.exports.optionsFile),
      tryOptions(process.env.FLAVORS_LOCAL_OPTIONS_PATH, module.exports.localOptionsFile)
    ]),
    ...rest
  };
}

function print(argv) {
  const {configName, options} = loadOptions(argv);
  process.stdout.write(JSON.stringify(require('.')(configName, options), null, 2) + '\n');
}

function run(argv) {
  const {configName, command, options} = loadOptions(argv);
  return require('./runner')(command, configName || process.env.FLAVORS_CONFIG_NAME, options);
}

module.exports = () => {
  const yargs = require('yargs');

  // noinspection BadExpressionStatementJS
  yargs// eslint-disable-line no-unused-expressions
    .strict()
    .parserConfiguration({
      'populate--': true
    })
    .usage('flavors - print loaded configuration or configure environment and run command.')
    .command({
      command: ['print', '*'],
      desc: 'Load and print configuration with specified name in JSON format',
      handler: print
    })
    .command({
      command: 'run <command> [args..]',
      desc: 'Load configuration and run command',
      builder: yargs => yargs.options({
        'plugin': {
          alias: 'p',
          boolean: true,
          describe: 'Treat command as Node.js module name or path.'
        },
        'shell': {
          boolean: true,
          default: require('./runner').defaultOptions.spawnOptions.shell,
          describe: 'Runs command inside of a shell. Uses /bin/sh on Unix, and process.env.ComSpec on Windows.',
        },
      }),
      handler: run
    })
    .options({
      'dir-name': {
        alias: 'd',
        describe: 'Configuration directory name'
      },
      'file-name': {
        alias: 'f',
        describe: 'Configuration file name (excluding extension)'
      },
      'loader': {
        alias: 'l',
        describe: 'Name of a Node.js module or a path to it'
      },
      'name': {
        alias: 'n',
        required: !process.env.FLAVORS_CONFIG_NAME,
        describe: 'Configuration name. Use this option or FLAVORS_CONFIG_NAME environment variable.'
      },
      'options-path': {
        alias: 'o',
        describe: 'Path to options file'
      },
      'separator': {
        alias: 's',
        describe: 'Configuration name separator'
      },
      'working-dir': {
        alias: 'w',
        describe: 'Directory name where configuration resolving starts from'
      }
    })
    .epilogue('for more information, please read our documentation at https://github.com/flavors-js/flavors.')
    .help()
    .version()
    .wrap(yargs.terminalWidth())
    .argv;
};

module.exports.localOptionsFile = 'flavorsOptions.local.js';
module.exports.optionsFile = 'flavorsOptions.js';

if (require.main === module) {
  module.exports();
}
<commit_msg>fix(*): resolving relative path to options file.<commit_after>#!/usr/bin/env node
'use strict';

function parseArgs(argv) {
  let loaders;
  if (argv.loader) {
    if (Array.isArray(argv.loader) && argv.loader.length > 0) {
      loaders = argv.loader;
    } else {
      loaders = [argv.loader];
    }
    loaders = loaders.map(require);
  }
  let command, plugin;
  if (argv.plugin) {
    plugin = require(argv.command);
  } else {
    command = argv.command;
  }
  const {optionsPath, shell, workingDir} = argv;
  return {
    command: {
      command,
      plugin,
      args: [...(argv.args || []), ...(argv['--'] || [])]
    },
    configName: argv.name,
    options: {
      configDirName: argv.dirName,
      configFileName: argv.fileName,
      configNameSeparator: argv.separator,
      loaders,
      spawnOptions: {
        shell
      },
      workingDir
    },
    optionsPath
  };
}

function resolveOptionsPath(customPath, optionsFile) {
  const path = require('path');

  return customPath
    ? (customPath.endsWith('.js')
      ? path.resolve(customPath)
      : path.resolve(customPath, optionsFile))
    : path.resolve(process.cwd(), optionsFile);
}

function tryOptions(customPath, optionsFile) {
  if (customPath || optionsFile) {
    try {
      return require(resolveOptionsPath(customPath, optionsFile));
    } catch (e) {
      if (e.code !== 'MODULE_NOT_FOUND' || customPath) {
        throw e;
      }
    }
  }
  return {};
}

function loadOptions(argv) {
  const {options: argvOptions, optionsPath, ...rest} = parseArgs(argv);
  return {
    options: require('deepmerge').all([
      {
        spawnOptions: {
          stdio: 'inherit'
        }
      },
      tryOptions(optionsPath, module.exports.optionsFile),
      argvOptions,
      tryOptions(process.env.FLAVORS_OPTIONS_PATH, module.exports.optionsFile),
      tryOptions(process.env.FLAVORS_LOCAL_OPTIONS_PATH, module.exports.localOptionsFile)
    ]),
    ...rest
  };
}

function print(argv) {
  const {configName, options} = loadOptions(argv);
  process.stdout.write(JSON.stringify(require('.')(configName, options), null, 2) + '\n');
}

function run(argv) {
  const {configName, command, options} = loadOptions(argv);
  return require('./runner')(command, configName || process.env.FLAVORS_CONFIG_NAME, options);
}

module.exports = () => {
  const yargs = require('yargs');

  // noinspection BadExpressionStatementJS
  yargs// eslint-disable-line no-unused-expressions
    .strict()
    .parserConfiguration({
      'populate--': true
    })
    .usage('flavors - print loaded configuration or configure environment and run command.')
    .command({
      command: ['print', '*'],
      desc: 'Load and print configuration with specified name in JSON format',
      handler: print
    })
    .command({
      command: 'run <command> [args..]',
      desc: 'Load configuration and run command',
      builder: yargs => yargs.options({
        'plugin': {
          alias: 'p',
          boolean: true,
          describe: 'Treat command as Node.js module name or path.'
        },
        'shell': {
          boolean: true,
          default: require('./runner').defaultOptions.spawnOptions.shell,
          describe: 'Runs command inside of a shell. Uses /bin/sh on Unix, and process.env.ComSpec on Windows.',
        },
      }),
      handler: run
    })
    .options({
      'dir-name': {
        alias: 'd',
        describe: 'Configuration directory name'
      },
      'file-name': {
        alias: 'f',
        describe: 'Configuration file name (excluding extension)'
      },
      'loader': {
        alias: 'l',
        describe: 'Name of a Node.js module or a path to it'
      },
      'name': {
        alias: 'n',
        required: !process.env.FLAVORS_CONFIG_NAME,
        describe: 'Configuration name. Use this option or FLAVORS_CONFIG_NAME environment variable.'
      },
      'options-path': {
        alias: 'o',
        describe: 'Path to options file'
      },
      'separator': {
        alias: 's',
        describe: 'Configuration name separator'
      },
      'working-dir': {
        alias: 'w',
        describe: 'Directory name where configuration resolving starts from'
      }
    })
    .epilogue('for more information, please read our documentation at https://github.com/flavors-js/flavors.')
    .help()
    .version()
    .wrap(yargs.terminalWidth())
    .argv;
};

module.exports.localOptionsFile = 'flavorsOptions.local.js';
module.exports.optionsFile = 'flavorsOptions.js';

if (require.main === module) {
  module.exports();
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, University of Colorado Boulder

/**
 * An accessible peer controls the appearance of an accessible Node's instance in the parallel DOM.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Jesse Greenberg
 */

define( function( require ) {
  'use strict';

  var inherit = require( 'PHET_CORE/inherit' );
  var Poolable = require( 'PHET_CORE/Poolable' );
  var Events = require( 'AXON/Events' );
  var scenery = require( 'SCENERY/scenery' );
  var Display = require( 'SCENERY/display/Display' );

  var globalId = 1;

  /**
   * Constructor.
   *
   * @param  {AccessibleInstance} accessibleInstance
   * @param  {DOMElement} domElement - The main DOM element used for this peer.
   * @param  {Object} options
   * @constructor
   */
  function AccessiblePeer( accessibleInstance, domElement, options ) {
    this.initializeAccessiblePeer( accessibleInstance, domElement, options );
  }

  scenery.register( 'AccessiblePeer', AccessiblePeer );

  inherit( Events, AccessiblePeer, {

    /**
     * @param {AccessibleInstance} accessibleInstance
     * @param {DOMElement} domElement - The main DOM element used for this peer.
     * @param {Object} [options]
     * @returns {AccessiblePeer} - Returns 'this' reference, for chaining
     */
    initializeAccessiblePeer: function( accessibleInstance, domElement, options ) {
      var self = this;

      options = _.extend( {
        parentContainerElement: null, // a parent container for this peer and potential siblings
        childContainerElement: null // an child container element where nested elements can be placed
      }, options );

      Events.call( this ); // TODO: is Events worth mixing in by default? Will we need to listen to events?

      assert && assert( !this.id || this.disposed, 'If we previously existed, we need to have been disposed' );

      // unique ID
      this.id = this.id || globalId++;

      // @public
      this.accessibleInstance = accessibleInstance;
      this.domElement = domElement;
      this.display = accessibleInstance.display;
      this.trail = accessibleInstance.trail;

      // @private - descendent of domElement that can be used to hold nested children
      this.childContainerElement = options.childContainerElement ? options.childContainerElement : ( this.childContainerElement || null );

      // @private - a parent element that can contain this domElement and other siblings
      this.parentContainerElement = options.parentContainerElement ? options.parentContainerElement : ( this.parentContainerElement || null );
      if ( this.parentContainerElement ) {

        // The first child of the parent container element should be the peer dom element
        // if undefined, the insertBefore method will insert the peerDOMElement as the first child
        var peerDOMElement = this.domElement;
        var firstChild = this.parentContainerElement.children[ 0 ];
        this.parentContainerElement.insertBefore( peerDOMElement, firstChild );
      }

      this.disposed = false;

      // @private - listener for the focus event, to be disposed
      self.focusEventListener = function( event ) {
        if ( event.target === self.domElement ) {
          Display.focus = {
            display: accessibleInstance.display,
            trail: accessibleInstance.trail
          };
        }
      };
      this.domElement.addEventListener( 'focus', self.focusEventListener );

      // @private - listener for the blur event, to be disposed
      self.blurEventListener = function( event ) {
        if ( event.target === self.domElement ) {
          Display.focus = null;
        }
      };
      this.domElement.addEventListener( 'blur', self.blurEventListener );

      return this;
    },

    /**
     * Check to see if this peer is contained in a parent container.
     *
     * @return {boolean}
     */
    hasParentContainer: function() {
      return !!this.parentContainerElement;
    },

    /**
     * Get the parent container or the peer's dom element direclty.  Used for sorting.
     *
     * @return {type}  description
     */
    getParentContainerElement: function() {
      return this.parentContainerElement || this.domElement;
    },

    /**
     * Get the child container or the peer's DOM element, used for sorting.
     *
     * @return {type}  description
     */
    getChildContainerElement: function() {
      return this.childContainerElement || this.domElement;
    },

    dispose: function() {
      this.disposed = true;

      this.domElement.removeEventListener( 'blur', this.blurEventListener );
      this.domElement.removeEventListener( 'focus', this.focusEventListener );

      // for now
      this.freeToPool && this.freeToPool();
    }
  } );

  // TODO: evaluate pooling, and is it OK to pool only some peers?
  AccessiblePeer.Poolable = {
    mixin: function( selfDrawableType ) {
      // for pooling, allow <AccessiblePeerType>.createFromPool( accessibleInstance ) and accessiblePeer.freeToPool().
      // Creation will initialize the peer to an initial state.
      Poolable.mixin( selfDrawableType, {
        defaultFactory: function() {
          return new selfDrawableType();
        },
        constructorDuplicateFactory: function( pool ) {
          return function( accessibleInstance ) {
            if ( pool.length ) {
              return pool.pop().initialize( accessibleInstance );
            }
            else {
              return new selfDrawableType( accessibleInstance );
            }
          };
        }
      } );
    }
  };

  return AccessiblePeer;
} );
<commit_msg>dispose accessible peers, see phetsims/scenery-phet#226<commit_after>// Copyright 2015-2016, University of Colorado Boulder

/**
 * An accessible peer controls the appearance of an accessible Node's instance in the parallel DOM.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Jesse Greenberg
 */

define( function( require ) {
  'use strict';

  var inherit = require( 'PHET_CORE/inherit' );
  var Poolable = require( 'PHET_CORE/Poolable' );
  var Events = require( 'AXON/Events' );
  var scenery = require( 'SCENERY/scenery' );
  var Display = require( 'SCENERY/display/Display' );

  var globalId = 1;

  /**
   * Constructor.
   *
   * @param  {AccessibleInstance} accessibleInstance
   * @param  {DOMElement} domElement - The main DOM element used for this peer.
   * @param  {Object} options
   * @constructor
   */
  function AccessiblePeer( accessibleInstance, domElement, options ) {
    this.initializeAccessiblePeer( accessibleInstance, domElement, options );
  }

  scenery.register( 'AccessiblePeer', AccessiblePeer );

  inherit( Events, AccessiblePeer, {

    /**
     * @param {AccessibleInstance} accessibleInstance
     * @param {DOMElement} domElement - The main DOM element used for this peer.
     * @param {Object} [options]
     * @returns {AccessiblePeer} - Returns 'this' reference, for chaining
     */
    initializeAccessiblePeer: function( accessibleInstance, domElement, options ) {
      var self = this;

      options = _.extend( {
        parentContainerElement: null, // a parent container for this peer and potential siblings
        childContainerElement: null // an child container element where nested elements can be placed
      }, options );

      Events.call( this ); // TODO: is Events worth mixing in by default? Will we need to listen to events?

      assert && assert( !this.id || this.disposed, 'If we previously existed, we need to have been disposed' );

      // unique ID
      this.id = this.id || globalId++;

      // @public
      this.accessibleInstance = accessibleInstance;
      this.domElement = domElement;
      this.display = accessibleInstance.display;
      this.trail = accessibleInstance.trail;

      // @private - descendent of domElement that can be used to hold nested children
      this.childContainerElement = options.childContainerElement ? options.childContainerElement : ( this.childContainerElement || null );

      // @private - a parent element that can contain this domElement and other siblings
      this.parentContainerElement = options.parentContainerElement ? options.parentContainerElement : ( this.parentContainerElement || null );
      if ( this.parentContainerElement ) {

        // The first child of the parent container element should be the peer dom element
        // if undefined, the insertBefore method will insert the peerDOMElement as the first child
        var peerDOMElement = this.domElement;
        var firstChild = this.parentContainerElement.children[ 0 ];
        this.parentContainerElement.insertBefore( peerDOMElement, firstChild );
      }

      this.disposed = false;

      // @private - listener for the focus event, to be disposed
      var focusEventListener = function( event ) {
        if ( event.target === self.domElement ) {
          Display.focus = {
            display: accessibleInstance.display,
            trail: accessibleInstance.trail
          };
        }
      };
      this.domElement.addEventListener( 'focus', focusEventListener );

      // @private - listener for the blur event, to be disposed
      var blurEventListener = function( event ) {
        if ( event.target === self.domElement ) {
          Display.focus = null;
        }
      };
      this.domElement.addEventListener( 'blur', blurEventListener );

      // make AccessiblePeer eligible for garabage collection
      this.disposeAccessiblePeer = function() {
        self.domElement.removeEventListener( 'blur', blurEventListener );
        self.domElement.removeEventListener( 'focus', focusEventListener );
      };

      return this;
    },

    /**
     * Check to see if this peer is contained in a parent container.
     *
     * @return {boolean}
     */
    hasParentContainer: function() {
      return !!this.parentContainerElement;
    },

    /**
     * Get the parent container or the peer's dom element direclty.  Used for sorting.
     *
     * @return {type}  description
     */
    getParentContainerElement: function() {
      return this.parentContainerElement || this.domElement;
    },

    /**
     * Get the child container or the peer's DOM element, used for sorting.
     *
     * @return {type}  description
     */
    getChildContainerElement: function() {
      return this.childContainerElement || this.domElement;
    },

    dispose: function() {
      this.disposed = true;
      this.disposeAccessiblePeer();

      // for now
      this.freeToPool && this.freeToPool();
    }
  } );

  // TODO: evaluate pooling, and is it OK to pool only some peers?
  AccessiblePeer.Poolable = {
    mixin: function( selfDrawableType ) {
      // for pooling, allow <AccessiblePeerType>.createFromPool( accessibleInstance ) and accessiblePeer.freeToPool().
      // Creation will initialize the peer to an initial state.
      Poolable.mixin( selfDrawableType, {
        defaultFactory: function() {
          return new selfDrawableType();
        },
        constructorDuplicateFactory: function( pool ) {
          return function( accessibleInstance ) {
            if ( pool.length ) {
              return pool.pop().initialize( accessibleInstance );
            }
            else {
              return new selfDrawableType( accessibleInstance );
            }
          };
        }
      } );
    }
  };

  return AccessiblePeer;
} );
<|endoftext|>


<commit_before>'use strict';

const MethodError = require('../error/methodError');
const Element = require('./element');
const EventEmitter = require('events');

module.exports = MatchModel;

function MatchModel() {
    let self = this;

    this.eventEmitter = new EventEmitter();
    this.eventEmitter.on('error', function() {});

    this.id = null;

    this.game = null;

    this.settings = {};

    this.state = 'open';

    this.masterUserId = null;

    this.slots = [];

    this.elements = [];

    this.notifications = [];

    let gameProcess = null;

    let gameSocket = null;

    let startTime = null;

    this.setGameProcess = function(input) {
        gameProcess = input;
    };
    this.getGameProcess = function() {
        return gameProcess;
    };

    this.setGameSocket = function(input) {
        gameSocket = input;
    };
    this.getGameSocket = function() {
        return gameSocket;
    };

    this.setState = function(state) {
        this.state = state;

        if (this.state === 'running') {
            startTime = Date.now();
        }
    };

    this.handleEvent = function(event, data) {
        if (event === 'element.added') {
            this.addElement(data);
        }

        if (event === 'element.removed') {
            this.removeElement(data);
        }

        if (event === 'element.moved') {
            this.moveElement(data);
        }

        if (event === 'slot.activeChanged') {
            this.slots[data.slotIndex].active = data.isActive;

            this.eventEmitter.emit('slotsChanged');
        }

        if (event === 'slot.pointsChanged') {
            this.slots[data.slotIndex].points = data.points;

            this.eventEmitter.emit('slotsChanged');
        }

        if (event === 'notification.added') {
            let notification = {
                time: startTime === null ? 0 : Math.floor((Date.now() - startTime) / 1000),
                text: data.text,
                targetPlayers: data.targetPlayers || null
            };
            this.notifications.push(notification);

            this.eventEmitter.emit('event', function(slotIndex) {
                if (!notification.targetPlayers || notification.targetPlayers.indexOf(slotIndex) > -1) {
                    return {
                        event: event,
                        time: notification.time,
                        text: notification.text
                    };
                }

                return null;
            });
        }

        if (typeof data.id !== 'undefined') {
            for (let i = 0; i < this.elements.length; i++) {
                if (this.elements[i].id === data.id && typeof this.elements[i].element.onEvent === 'function') {
                    let frontendData = this.elements[i].element.onEvent(event, data);

                    if (frontendData !== null && frontendData !== undefined) {
                        this.eventEmitter.emit('event', function (slotIndex) {
                            let returnData = {
                                event: event,
                                id: data.id
                            };

                            let playerData = frontendData;
                            if (typeof frontendData === 'function') {
                                playerData = frontendData(slotIndex);
                            }

                            for (let i in playerData) {
                                returnData[i] = playerData[i];
                            }

                            return returnData;
                        });
                    }
                }
            }
        }
    };

    this.addElement = function(data) {
        const ElementType = require('../game/element/' + data.type);

        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === data.id) {
                throw new Error('elementAlreadyExisting');
            }
        }

        let element = new Element();
        element.id = data.id;
        element.type = data.type;
        element.parent = data.parent;
        element.element = new ElementType(data.element);

        this.elements.push(element);

        this.eventEmitter.emit('event', function(slotIndex) {
            return {
                event: 'element.added',
                id: element.id,
                type: element.type,
                parent: element.parent,
                element: element.element.transformToFrontend(slotIndex)
            };
        });
    };

    this.transformToFrontend = function(slotIndex) {
        let sanitizedElements = [];

        for (let i = 0; i < this.elements.length; i++) {
            sanitizedElements.push({
                id: this.elements[i].id,
                type: this.elements[i].type,
                parent: this.elements[i].parent,
                element: this.elements[i].element.transformToFrontend(slotIndex)
            });
        }

        let sanitizedNotifications = [];
        for (let i = 0; i < this.notifications.length; i++) {
            if (!this.notifications[i].targetPlayers || this.notifications[i].targetPlayers.indexOf(slotIndex) > -1) {
                sanitizedNotifications.push({
                    time: this.notifications[i].time,
                    text: this.notifications[i].text
                });
            }
        }

        return {
            id: this.id,
            game: this.game,
            settings: this.settings,
            state: this.state,
            masterUserId: this.masterUserId,
            slots: this.slots,
            elements: sanitizedElements,
            notifications: sanitizedNotifications
        };
    };

    this.removeElement = function(data) {
        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === data.id) {
                this.elements.splice(i, 1);

                this.eventEmitter.emit('event', {
                    event: 'element.removed',
                    id: data.id
                });

                return;
            }
        }

       throw new Error('elementNotExisting');
    };

    this.moveElement = function(data) {
        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === data.id) {
                this.elements[i].parent = data.parent;

                this.eventEmitter.emit('event', {
                    event: 'element.moved',
                    id: data.id,
                    parent: data.parent,
                    duration: data.duration
                });

                return;
            }
        }

        throw new Error('elementNotExisting');
    };

    this.handleElementMethod = function(elementId, method, data, slotIndex) {
        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === elementId) {
                if (typeof this.elements[i].element.onMethod !== 'function') {
                    throw new MethodError('method.methodNotFound');
                }

                return this.elements[i].element.onMethod(method, data, slotIndex);
            }
        }

        throw new MethodError('method.elementNotFound');
    };
}<commit_msg>Added a "statusMessage", so everyone knows always, who has to do what.<commit_after>'use strict';

const MethodError = require('../error/methodError');
const Element = require('./element');
const EventEmitter = require('events');

module.exports = MatchModel;

function MatchModel() {
    let self = this;

    this.eventEmitter = new EventEmitter();
    this.eventEmitter.on('error', function() {});

    this.id = null;

    this.game = null;

    this.settings = {};

    this.state = 'open';

    this.masterUserId = null;

    this.slots = [];

    this.elements = [];

    this.statusMessage = '';

    this.notifications = [];

    let gameProcess = null;

    let gameSocket = null;

    let startTime = null;

    this.setGameProcess = function(input) {
        gameProcess = input;
    };
    this.getGameProcess = function() {
        return gameProcess;
    };

    this.setGameSocket = function(input) {
        gameSocket = input;
    };
    this.getGameSocket = function() {
        return gameSocket;
    };

    this.setState = function(state) {
        this.state = state;

        if (this.state === 'running') {
            startTime = Date.now();
        }
    };

    this.handleEvent = function(event, data) {
        if (event === 'element.added') {
            this.addElement(data);
        }

        if (event === 'element.removed') {
            this.removeElement(data);
        }

        if (event === 'element.moved') {
            this.moveElement(data);
        }

        if (event === 'slot.activeChanged') {
            this.slots[data.slotIndex].active = data.isActive;

            this.eventEmitter.emit('slotsChanged');
        }

        if (event === 'slot.pointsChanged') {
            this.slots[data.slotIndex].points = data.points;

            this.eventEmitter.emit('slotsChanged');
        }

        if (event === 'statusMessage.changed') {
            this.statusMessage = data.text;

            this.eventEmitter.emit('event', function(slotIndex) {
                return {
                    event: event,
                    text: data.text
                };
            });
        }

        if (event === 'notification.added') {
            let notification = {
                time: startTime === null ? 0 : Math.floor((Date.now() - startTime) / 1000),
                text: data.text,
                targetPlayers: data.targetPlayers || null
            };
            this.notifications.push(notification);

            this.eventEmitter.emit('event', function(slotIndex) {
                if (!notification.targetPlayers || notification.targetPlayers.indexOf(slotIndex) > -1) {
                    return {
                        event: event,
                        time: notification.time,
                        text: notification.text
                    };
                }

                return null;
            });
        }

        if (typeof data.id !== 'undefined') {
            for (let i = 0; i < this.elements.length; i++) {
                if (this.elements[i].id === data.id && typeof this.elements[i].element.onEvent === 'function') {
                    let frontendData = this.elements[i].element.onEvent(event, data);

                    if (frontendData !== null && frontendData !== undefined) {
                        this.eventEmitter.emit('event', function (slotIndex) {
                            let returnData = {
                                event: event,
                                id: data.id
                            };

                            let playerData = frontendData;
                            if (typeof frontendData === 'function') {
                                playerData = frontendData(slotIndex);
                            }

                            for (let i in playerData) {
                                returnData[i] = playerData[i];
                            }

                            return returnData;
                        });
                    }
                }
            }
        }
    };

    this.addElement = function(data) {
        const ElementType = require('../game/element/' + data.type);

        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === data.id) {
                throw new Error('elementAlreadyExisting');
            }
        }

        let element = new Element();
        element.id = data.id;
        element.type = data.type;
        element.parent = data.parent;
        element.element = new ElementType(data.element);

        this.elements.push(element);

        this.eventEmitter.emit('event', function(slotIndex) {
            return {
                event: 'element.added',
                id: element.id,
                type: element.type,
                parent: element.parent,
                element: element.element.transformToFrontend(slotIndex)
            };
        });
    };

    this.transformToFrontend = function(slotIndex) {
        let sanitizedElements = [];

        for (let i = 0; i < this.elements.length; i++) {
            sanitizedElements.push({
                id: this.elements[i].id,
                type: this.elements[i].type,
                parent: this.elements[i].parent,
                element: this.elements[i].element.transformToFrontend(slotIndex)
            });
        }

        let sanitizedNotifications = [];
        for (let i = 0; i < this.notifications.length; i++) {
            if (!this.notifications[i].targetPlayers || this.notifications[i].targetPlayers.indexOf(slotIndex) > -1) {
                sanitizedNotifications.push({
                    time: this.notifications[i].time,
                    text: this.notifications[i].text
                });
            }
        }

        return {
            id: this.id,
            game: this.game,
            settings: this.settings,
            state: this.state,
            masterUserId: this.masterUserId,
            slots: this.slots,
            elements: sanitizedElements,
            statusMessage: this.statusMessage,
            notifications: sanitizedNotifications
        };
    };

    this.removeElement = function(data) {
        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === data.id) {
                this.elements.splice(i, 1);

                this.eventEmitter.emit('event', {
                    event: 'element.removed',
                    id: data.id
                });

                return;
            }
        }

       throw new Error('elementNotExisting');
    };

    this.moveElement = function(data) {
        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === data.id) {
                this.elements[i].parent = data.parent;

                this.eventEmitter.emit('event', {
                    event: 'element.moved',
                    id: data.id,
                    parent: data.parent,
                    duration: data.duration
                });

                return;
            }
        }

        throw new Error('elementNotExisting');
    };

    this.handleElementMethod = function(elementId, method, data, slotIndex) {
        for (let i = 0; i < this.elements.length; i++) {
            if (this.elements[i].id === elementId) {
                if (typeof this.elements[i].element.onMethod !== 'function') {
                    throw new MethodError('method.methodNotFound');
                }

                return this.elements[i].element.onMethod(method, data, slotIndex);
            }
        }

        throw new MethodError('method.elementNotFound');
    };
}<|endoftext|>


<commit_before>var util = require('../util');
var rulesUtil = require('./util');
var url = require('url');
var net = require('net');
var dns = require('dns');
var Q = require('q');
var extend = require('util')._extend;
var DNS_CACHE = {};
var DNS_CACHE_TIME = 60000;
var allowDnsCache = true;

var rules, hosts, heads, req, res, proxy, weinre, filter;
rules = hosts = heads = req = res = proxy = weinre = filter = [];

function parse(text) {
	rules = [];
	hosts = [];
	heads = [];
	req = [];
	res = [];
	proxy = [];
	weinre = [];
	filter = [];
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^(?:http|ws)s?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var rule = {
			isRegExp: isRegExp,
			protocol: isRegExp ? null : util.getProtocol(pattern),
			pattern: isRegExp ? pattern : formatUrl(pattern),
			matcher: matcher,
			raw: raw,
			rawPattern: pattern
		};

	if (net.isIP(matcher)) {
		hosts.push(rule);
	} else if (/^head:\/\//.test(matcher)) {
		heads.push(rule);
	} else if (/^req:\/\//.test(matcher)) {
		req.push(rule);
	} else if (/^res:\/\//.test(matcher)) {
		res.push(rule);
	} else if (/^(?:proxy|socks):\/\//.test(matcher)) {
		proxy.push(rule);
	} else if (/^weinre:\/\//.test(matcher)) {
		weinre.push(rule);
	} else if (/^filter:\/\//.test(matcher)) {
		filter.push(rule);
	} else {
		rules.push(rule);
	}
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!util.isWebsocketProtocol(options.protocol) && !util.isWebProtocol(options.protocol)) {

		return callback(null, null);
	}

	var host = getRule(_url, hosts);
	if (host) {
		return callback(null, host.matcher, true);
	}

	var hostname = options.hostname;
	var promise = DNS_CACHE[hostname];
	var defer, done;
	if (!promise || (promise.time && Date.now() - promise.time > DNS_CACHE_TIME)) {
		defer = Q.defer();
		promise = DNS_CACHE[hostname] = defer.promise;
	}

	promise.done(function(ip) {
		callback(null, ip);
	}, callback);

	if (!defer) {
		return;
	}

	(function lookup() {
		try {
			dns.lookup(hostname, function (err, ip, addressType) {
				if (err) {
					if (!done) {
						done = true;
						lookup();
					} else {
						DNS_CACHE[hostname] = null;
						defer.reject(err);
					}
					return;
				}
				promise.time = Date.now();
				defer.resolve(ip || (!addressType || addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1'));
			  });
		} catch(err) {//如果断网，可能直接抛异常，https代理没有用到error-handler
			DNS_CACHE[hostname] = null;
			defer.reject(err);
		}
	})();
}

function formatUrl(pattern) {

	return pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern;
}

exports.resolveHost = resolveHost;

function getRule(url, _rules) {
	var rules = _getRule(url, _rules);
	//支持域名匹配
	if (!rules && (rules = _getRule(url.replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), _rules))
			&& util.removeProtocol(rules.rawPattern, true).indexOf('/') != -1) {
		rules = null;
	}
	if (rules) {
		url = rules.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rules.key = key;
		}
		var value = key && rulesUtil.getValue(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rules.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rules.path = protocol + value;
			rules.files = getFiles(rules.path);
		}
	}

	return rules;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, _rules) {
	_rules = _rules || rules;
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}

	var _url = url.replace(/(?:\?|#).*$/, '');
	for (var i = 0, rule; rule = _rules[i]; i++) {
		var pattern = rule.pattern;
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				return extend({
					url: setProtocol(rule.matcher.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					}), url)
				}, rule);;
			}
		} else {
			pattern = setProtocol(pattern, url);
			if (pattern.indexOf('?') != -1 && url.indexOf(pattern) === 0) {
				var matcher = setProtocol(rule.matcher, url);
				return extend({
					url: matcher.indexOf('?') == -1 ? matcher : matcher + url.substring(pattern.length)
				}, rule);
			}

			if (_url.indexOf(pattern) === 0) {
				var len = pattern.length;
				if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
					var matcher = setProtocol(rule.matcher, url);
					var filePath = url.substring(len);
					var files = getFiles(matcher);
					if (files) {
						files = files.map(function(file) {
							return join(file, filePath);
						});
					}

					return extend({
						files: files,
						url: join(matcher, filePath)
					}, rule);
				}
			}
		}
	}
}

function getFiles(path) {

	return /^x?file:/.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function resolveRule(url) {
	return getRule(url);
}

function resolveHead(url) {
	return getRule(url, heads);
}

function resolveReq(url) {
	return getRule(url, req);
}

function resolveRes(url) {
	return getRule(url, res);
}

function resolveProxy(url) {
	return getRule(url, proxy);
}

function resolveWeinre(url) {
	return getRule(url, weinre);
}

function resolveFilter(url) {
	return getRule(url, filter);
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	var len = first.length - 1;
	if (isPathSeparator(first[len])) {
		return isPathSeparator(last[0]) ? first.substring(0, len) + last : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rules = {};
	var rule = resolveRule(url);
	if (rule) {
		rules.rule = rule;
	}
	if (rule = resolveHead(url)) {
		rules.head = rule;
	}
	if (rule = resolveReq(url)) {
		rules.req = rule;
	}
	if (rule = resolveRes(url)) {
		rules.res = rule;
	}
	if (rule = resolveProxy(url)) {
		rules.proxy = rule;
	}
	if (rule = resolveWeinre(url)) {
		rules.weinre = rule;
	}
	if (rule = resolveFilter(url)) {
		rules.filter = rule;
	}

	return rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<commit_msg>filters<commit_after>var util = require('../util');
var rulesUtil = require('./util');
var url = require('url');
var net = require('net');
var dns = require('dns');
var Q = require('q');
var extend = require('util')._extend;
var DNS_CACHE = {};
var DNS_CACHE_TIME = 60000;
var allowDnsCache = true;

var rules, hosts, heads, req, res, proxy, weinre, filter;
rules = hosts = heads = req = res = proxy = weinre = filter = [];

function parse(text) {
	rules = [];
	hosts = [];
	heads = [];
	req = [];
	res = [];
	proxy = [];
	weinre = [];
	filter = [];
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^(?:http|ws)s?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var rule = {
			isRegExp: isRegExp,
			protocol: isRegExp ? null : util.getProtocol(pattern),
			pattern: isRegExp ? pattern : formatUrl(pattern),
			matcher: matcher,
			raw: raw,
			rawPattern: pattern
		};

	if (net.isIP(matcher)) {
		hosts.push(rule);
	} else if (/^head:\/\//.test(matcher)) {
		heads.push(rule);
	} else if (/^req:\/\//.test(matcher)) {
		req.push(rule);
	} else if (/^res:\/\//.test(matcher)) {
		res.push(rule);
	} else if (/^(?:proxy|socks):\/\//.test(matcher)) {
		proxy.push(rule);
	} else if (/^weinre:\/\//.test(matcher)) {
		weinre.push(rule);
	} else if (/^filter:\/\//.test(matcher)) {
		filter.push(rule);
	} else {
		rules.push(rule);
	}
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!util.isWebsocketProtocol(options.protocol) && !util.isWebProtocol(options.protocol)) {

		return callback(null, null);
	}

	var host = getRule(_url, hosts);
	if (host) {
		return callback(null, host.matcher, true);
	}

	var hostname = options.hostname;
	var promise = DNS_CACHE[hostname];
	var defer, done;
	if (!promise || (promise.time && Date.now() - promise.time > DNS_CACHE_TIME)) {
		defer = Q.defer();
		promise = DNS_CACHE[hostname] = defer.promise;
	}

	promise.done(function(ip) {
		callback(null, ip);
	}, callback);

	if (!defer) {
		return;
	}

	(function lookup() {
		try {
			dns.lookup(hostname, function (err, ip, addressType) {
				if (err) {
					if (!done) {
						done = true;
						lookup();
					} else {
						DNS_CACHE[hostname] = null;
						defer.reject(err);
					}
					return;
				}
				promise.time = Date.now();
				defer.resolve(ip || (!addressType || addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1'));
			  });
		} catch(err) {//如果断网，可能直接抛异常，https代理没有用到error-handler
			DNS_CACHE[hostname] = null;
			defer.reject(err);
		}
	})();
}

function formatUrl(pattern) {

	return pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern;
}

exports.resolveHost = resolveHost;

function getRule(url, _rules) {
	var rules = _getRule(url, _rules);
	//支持域名匹配
	if (!rules && (rules = _getRule(url.replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), _rules))
			&& util.removeProtocol(rules.rawPattern, true).indexOf('/') != -1) {
		rules = null;
	}
	if (rules) {
		url = rules.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rules.key = key;
		}
		var value = key && rulesUtil.getValue(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rules.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rules.path = protocol + value;
			rules.files = getFiles(rules.path);
		}
	}

	return rules;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, _rules) {
	_rules = _rules || rules;
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}

	var _url = url.replace(/(?:\?|#).*$/, '');
	for (var i = 0, rule; rule = _rules[i]; i++) {
		var pattern = rule.pattern;
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				return extend({
					url: setProtocol(rule.matcher.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					}), url)
				}, rule);;
			}
		} else {
			pattern = setProtocol(pattern, url);
			if (pattern.indexOf('?') != -1 && url.indexOf(pattern) === 0) {
				var matcher = setProtocol(rule.matcher, url);
				return extend({
					url: matcher.indexOf('?') == -1 ? matcher : matcher + url.substring(pattern.length)
				}, rule);
			}

			if (_url.indexOf(pattern) === 0) {
				var len = pattern.length;
				if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
					var matcher = setProtocol(rule.matcher, url);
					var filePath = url.substring(len);
					var files = getFiles(matcher);
					if (files) {
						files = files.map(function(file) {
							return join(file, filePath);
						});
					}

					return extend({
						files: files,
						url: join(matcher, filePath)
					}, rule);
				}
			}
		}
	}
}

function getFiles(path) {

	return /^x?file:/.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function resolveRule(url) {
	return getRule(url);
}

function resolveHead(url) {
	return getRule(url, heads);
}

function resolveReq(url) {
	return getRule(url, req);
}

function resolveRes(url) {
	return getRule(url, res);
}

function resolveProxy(url) {
	return getRule(url, proxy);
}

function resolveWeinre(url) {
	return getRule(url, weinre);
}

function resolveFilter(url) {
	return getRule(url, filter);
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	var len = first.length - 1;
	if (isPathSeparator(first[len])) {
		return isPathSeparator(last[0]) ? first.substring(0, len) + last : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

exports.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rules = {};
	var rule = resolveRule(url);
	if (rule) {
		rules.rule = rule;
	}
	if (rule = resolveHead(url)) {
		rules.head = rule;
	}
	if (rule = resolveReq(url)) {
		rules.req = rule;
	}
	if (rule = resolveRes(url)) {
		rules.res = rule;
	}
	if (rule = resolveProxy(url)) {
		rules.proxy = rule;
	}
	if (rule = resolveWeinre(url)) {
		rules.weinre = rule;
	}
	if (rule = resolveFilter(url)) {
		var actions = util.removeProtocol(util.rule.getMatcher(rule), true);
		actions.split('|').forEach(function(action) {
			rule[action] = true;
		});
		rules.filter = rule;
	}

	return rules;
};

exports.disableDnsCache = function() {
	allowDnsCache = false;
};
<|endoftext|>