<commit_before>var Bittrex = require('node.bittrex.api');
var util = require('../core/util.js');
var _ = require('lodash');
var moment = require('moment');
var log = require('../core/log');
var apiKeyManager = require('../web/apiKeyManager.js');

// Helper methods
function joinCurrencies(currencyA, currencyB){
    return currencyA + '-' + currencyB;
}

var Trader = function(config) {
  _.bindAll(this);

  // not nice but works to use a web module here to grab the keys
  var keys = apiKeyManager._getApiKeyPair('bittrex');

  if(_.isObject(keys)) {
    config.key = keys.key;
    config.secret = keys.secret;
  } else {
    // no api key defined -> we need to set a dummy key, otherwise the Bittrex module will not work even for public requests
    config.key = 'sdfsdf32424dfdb';
    config.secret = 'sdfsdf32424dfdb';
  }

  if(_.isObject(config)) {
    this.key = config.key;
    this.secret = config.secret;
    this.currency = config.currency;
    this.asset = config.asset;
  }

  this.name = 'Bittrex';
  this.balance;
  this.price;

  this.pair = [this.currency, this.asset].join('-');

  Bittrex.options({
    'apikey':  this.key,
    'apisecret': this.secret,
    'stream': false,
    'verbose': false,
    'cleartext': false
  });

  this.logAction('Init', 'New Trader', {currency: this.currency, asset: this.asset});

  this.bittrexApi = Bittrex;
}

// if the exchange errors we try the same call again after
// waiting 10 seconds
Trader.prototype.retry = function(method, args) {
  var wait = +moment.duration(10, 'seconds');
  log.debug(this.name, 'returned an error, retrying..');

  this.logAction('retry', 'returned an error, retrying..');

  var self = this;

  // make sure the callback (and any other fn)
  // is bound to Trader
  _.each(args, function(arg, i) {
    if(_.isFunction(arg))
      args[i] = _.bind(arg, self);
  });

  // run the failed method again with the same
  // arguments after wait
  setTimeout(
    function() { method.apply(self, args) },
    wait
  );
}

Trader.prototype.getPortfolio = function(callback) {
  var args = _.toArray(arguments);
  this.logAction('getPortfolio', 'called');

  var set = function(data, err) {
    if(err)
      return this.retry(this.getPortfolio, args);

    data = data.result;

    var assetEntry = _.find(data, function(i) { return i.Currency == this.asset}.bind(this));
    var currencyEntry = _.find(data, function(i) { return i.Currency == this.currency}.bind(this));

    if(_.isUndefined(assetEntry)) {
      assetEntry = {
        Available: 0.0,
        Currency: this.asset
      }
    }

    if(_.isUndefined(currencyEntry)) {
      currencyEntry = {
        Available: 0.0,
        Currency: this.currency
      }
    }

    var assetAmount = parseFloat( assetEntry.Available );
    var currencyAmount = parseFloat( currencyEntry.Available );

    if(
      !_.isNumber(assetAmount) || _.isNaN(assetAmount) ||
      !_.isNumber(currencyAmount) || _.isNaN(currencyAmount)
    ) {
      log.info('asset:', this.asset);
      log.info('currency:', this.currency);
      log.info('exchange data:', data);
      util.die('Gekko was unable to set the portfolio');
    }

    var portfolio = [
      { name: this.asset, amount: assetAmount },
      { name: this.currency, amount: currencyAmount }
    ];

    this.logAction('getPortfolio', 'result:', portfolio);

    callback(err, portfolio);
  }.bind(this);

  this.bittrexApi.getbalances(set);
}

Trader.prototype.getTicker = function(callback) {
  var args = _.toArray(arguments);

  this.logAction('getTicker', 'called');

  this.bittrexApi.getticker({market: this.pair}, function(data, err) {
    if(err)
      return this.retry(this.getTicker, args);

    var tick = data.result;

    this.logAction('getTicker', 'result', tick);

    callback(null, {
      bid: parseFloat(tick.Bid),
      ask: parseFloat(tick.Ask),
    })

  }.bind(this));
}

Trader.prototype.getFee = function(callback) {

  this.logAction('getFee', 'called');
  /*var set = function(data, err) {
    if(err || data.error)
      return callback(err || data.error);

    data = data.result;

    var assetEntry = _.find(data, function(c) { return c.Currency == this.asset}.bind(this));
    //var currencyEntry = _.find(data, function(c) { return c.Currency == this.currency}.bind(this));

    var fee = parseFloat(assetEntry.TxFee);

    callback(false, parseFloat(assetEntry.TxFee));
  }.bind(this);

  this.bittrexApi.getcurrencies(set); */

   callback(false, parseFloat(0.00025));
}

Trader.prototype.buy = function(amount, price, callback) {
  var args = _.toArray(arguments);

   this.logAction('buy', 'called');

  var set = function(result, err) {
    if(err || result.error) {
      log.error('unable to buy:', err, result);
      return this.retry(this.buy, args);
    }

    this.logAction('buy', 'result', result);

    callback(null, result.result.uuid);
  }.bind(this);

  this.bittrexApi.buylimit({market: this.pair, quantity: amount, rate: price}, set);
}

Trader.prototype.sell = function(amount, price, callback) {
  var args = _.toArray(arguments);

  this.logAction('sell', 'called');

  var set = function(result, err) {
    if(err || result.error) {
      log.error('unable to sell:', err, result);
      return this.retry(this.sell, args);
    }

   this.logAction('sell', 'result', result);

    callback(null, result.result.uuid);
  }.bind(this);

  this.bittrexApi.selllimit({market: this.pair, quantity: amount, rate: price}, set);
}

Trader.prototype.checkOrder = function(order, callback) {
  var check = function(result, err) {
    this.logAction('checkOrder', 'called');

    var stillThere = _.find(result.result, function(o) { return o.OrderUuid === order });

    this.logAction('checkOrder', 'result', stillThere);
    callback(err, !stillThere);
  }.bind(this);

  this.bittrexApi.getopenorders({market: this.pair}, check);
}

Trader.prototype.getOrder = function(order, callback) {

  var get = function(result, errors) {

    this.logAction('getOrder', 'called');

    if(err)
      return callback(err);

    var price = 0;
    var amount = 0;
    var date = moment(0);

    if(!result.success)
      return callback(null, {price, amount, date});

    var resultOrder = result.result;

    price = resultOrder.Price;
    amount = resultOrder.Quantity;

    if(resultOrder.IsOpen) {
         date = moment(resultOrder.Opened);
    } else {
       date = moment(resultOrder.Closed);
    }

    this.logAction('getOrder', 'result', {price, amount, date});
    callback(err, {price, amount, date});
  }.bind(this);

  this.bittrexApi.getorder({uuid: order}, get);
}

Trader.prototype.cancelOrder = function(order, callback) {
  var args = _.toArray(arguments);
  var cancel = function(result, err) {
    this.logAction('cancelOrder', 'called', order);

    if(err || !result.success) {
      log.error('unable to cancel order', order, '(', err, result, '), retrying');
      return this.retry(this.cancelOrder, args);
    }

    this.logAction('getOrder', 'result', result);

    callback();
  }.bind(this);

  this.bittrexApi.cancel({uuid: order}, cancel);
}

Trader.prototype.getTrades = function(since, callback, descending) {

  var firstFetch = !!since;

  var args = _.toArray(arguments);
  var process = function(data, err) {
    if(err) {
      return this.retry(this.getTrades, args);
    }

    let result = data.result;

    // Edge case, see here:
    // @link https://github.com/askmike/gekko/issues/479
    if(firstFetch && _.size(result) === 50000)
      util.die(
        [
          'Poloniex did not provide enough data. Read this:',
          'https://github.com/askmike/gekko/issues/479'
        ].join('\n\n')
      );

      result = _.map(result, function(trade) {
        let mr = {
            tid: trade.Id,
            amount: +trade.Quantity,
            date: moment.utc(trade.TimeStamp).unix(),
            timeStamp: trade.TimeStamp,
            price: +trade.Price
        };
    	return mr;
    });

    callback(null, result.reverse());
  }.bind(this);

  var params = {
    currencyPair: joinCurrencies(this.currency, this.asset)
  }

  if(since)
    params.start = since.unix();

  this.bittrexApi.getmarkethistory({ market: params.currencyPair }, process);
}

Trader.getCapabilities = function () {
  return {
    name: 'bittrex',
    slug: 'bittrex',
    currencies: ['BTC', 'ETH', 'USDT'],
    assets: [
      'BTC', 'BCC','ETH','ANS','BCC'
    ],
    markets: [
      // *** BTC <-> XXX
      { pair: ['BTC', 'BCC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['BTC', 'ANS'], minimalOrder: { amount: 0.0001, unit: 'asset' } },

      // *** USDT <-> XXX
      { pair: ['USDT', 'BTC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['USDT', 'BCC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['USDT', 'ETH'], minimalOrder: { amount: 0.0001, unit: 'asset' } },

      // *** ETH <-> XXX
      { pair: ['ETH', 'BCC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['ETH', 'ANS'], minimalOrder: { amount: 0.0001, unit: 'asset' } }

    ],
    requires: ['key', 'secret'],
    tid: 'tid',
    providesHistory: 'date',
    providesFullHistory: true,
    tradable: true
  };
}

Trader.prototype.logAction = function(method, msg, obj) {
  console.info(this.name +' ' + method + ': ' + msg);
  if(_.isObject(obj)) {
    console.info(obj);
  }
}

module.exports = Trader;
<commit_msg>fix in method bittrex getOrder()<commit_after>var Bittrex = require('node.bittrex.api');
var util = require('../core/util.js');
var _ = require('lodash');
var moment = require('moment');
var log = require('../core/log');
var apiKeyManager = require('../web/apiKeyManager.js');

// Helper methods
function joinCurrencies(currencyA, currencyB){
    return currencyA + '-' + currencyB;
}

var Trader = function(config) {
  _.bindAll(this);

  // not nice but works to use a web module here to grab the keys
  var keys = apiKeyManager._getApiKeyPair('bittrex');

  if(_.isObject(keys)) {
    config.key = keys.key;
    config.secret = keys.secret;
  } else {
    // no api key defined -> we need to set a dummy key, otherwise the Bittrex module will not work even for public requests
    config.key = 'sdfsdf32424dfdb';
    config.secret = 'sdfsdf32424dfdb';
  }

  if(_.isObject(config)) {
    this.key = config.key;
    this.secret = config.secret;
    this.currency = config.currency;
    this.asset = config.asset;
  }

  this.name = 'Bittrex';
  this.balance;
  this.price;

  this.pair = [this.currency, this.asset].join('-');

  Bittrex.options({
    'apikey':  this.key,
    'apisecret': this.secret,
    'stream': false,
    'verbose': false,
    'cleartext': false
  });

  this.logAction('Init', 'New Trader', {currency: this.currency, asset: this.asset});

  this.bittrexApi = Bittrex;
}

// if the exchange errors we try the same call again after
// waiting 10 seconds
Trader.prototype.retry = function(method, args) {
  var wait = +moment.duration(10, 'seconds');
  log.debug(this.name, 'returned an error, retrying..');

  this.logAction('retry', 'returned an error, retrying..');

  var self = this;

  // make sure the callback (and any other fn)
  // is bound to Trader
  _.each(args, function(arg, i) {
    if(_.isFunction(arg))
      args[i] = _.bind(arg, self);
  });

  // run the failed method again with the same
  // arguments after wait
  setTimeout(
    function() { method.apply(self, args) },
    wait
  );
}

Trader.prototype.getPortfolio = function(callback) {
  var args = _.toArray(arguments);
  this.logAction('getPortfolio', 'called');

  var set = function(data, err) {
    if(err)
      return this.retry(this.getPortfolio, args);

    data = data.result;

    var assetEntry = _.find(data, function(i) { return i.Currency == this.asset}.bind(this));
    var currencyEntry = _.find(data, function(i) { return i.Currency == this.currency}.bind(this));

    if(_.isUndefined(assetEntry)) {
      assetEntry = {
        Available: 0.0,
        Currency: this.asset
      }
    }

    if(_.isUndefined(currencyEntry)) {
      currencyEntry = {
        Available: 0.0,
        Currency: this.currency
      }
    }

    var assetAmount = parseFloat( assetEntry.Available );
    var currencyAmount = parseFloat( currencyEntry.Available );

    if(
      !_.isNumber(assetAmount) || _.isNaN(assetAmount) ||
      !_.isNumber(currencyAmount) || _.isNaN(currencyAmount)
    ) {
      log.info('asset:', this.asset);
      log.info('currency:', this.currency);
      log.info('exchange data:', data);
      util.die('Gekko was unable to set the portfolio');
    }

    var portfolio = [
      { name: this.asset, amount: assetAmount },
      { name: this.currency, amount: currencyAmount }
    ];

    this.logAction('getPortfolio', 'result:', portfolio);

    callback(err, portfolio);
  }.bind(this);

  this.bittrexApi.getbalances(set);
}

Trader.prototype.getTicker = function(callback) {
  var args = _.toArray(arguments);

  this.logAction('getTicker', 'called');

  this.bittrexApi.getticker({market: this.pair}, function(data, err) {
    if(err)
      return this.retry(this.getTicker, args);

    var tick = data.result;

    this.logAction('getTicker', 'result', tick);

    callback(null, {
      bid: parseFloat(tick.Bid),
      ask: parseFloat(tick.Ask),
    })

  }.bind(this));
}

Trader.prototype.getFee = function(callback) {

  this.logAction('getFee', 'called');
  /*var set = function(data, err) {
    if(err || data.error)
      return callback(err || data.error);

    data = data.result;

    var assetEntry = _.find(data, function(c) { return c.Currency == this.asset}.bind(this));
    //var currencyEntry = _.find(data, function(c) { return c.Currency == this.currency}.bind(this));

    var fee = parseFloat(assetEntry.TxFee);

    callback(false, parseFloat(assetEntry.TxFee));
  }.bind(this);

  this.bittrexApi.getcurrencies(set); */

   callback(false, parseFloat(0.00025));
}

Trader.prototype.buy = function(amount, price, callback) {
  var args = _.toArray(arguments);

   this.logAction('buy', 'called');

  var set = function(result, err) {
    if(err || result.error) {
      log.error('unable to buy:', err, result);
      return this.retry(this.buy, args);
    }

    this.logAction('buy', 'result', result);

    callback(null, result.result.uuid);
  }.bind(this);

  this.bittrexApi.buylimit({market: this.pair, quantity: amount, rate: price}, set);
}

Trader.prototype.sell = function(amount, price, callback) {
  var args = _.toArray(arguments);

  this.logAction('sell', 'called');

  var set = function(result, err) {
    if(err || result.error) {
      log.error('unable to sell:', err, result);
      return this.retry(this.sell, args);
    }

   this.logAction('sell', 'result', result);

    callback(null, result.result.uuid);
  }.bind(this);

  this.bittrexApi.selllimit({market: this.pair, quantity: amount, rate: price}, set);
}

Trader.prototype.checkOrder = function(order, callback) {
  var check = function(result, err) {
    this.logAction('checkOrder', 'called');

    var stillThere = _.find(result.result, function(o) { return o.OrderUuid === order });

    this.logAction('checkOrder', 'result', stillThere);
    callback(err, !stillThere);
  }.bind(this);

  this.bittrexApi.getopenorders({market: this.pair}, check);
}

Trader.prototype.getOrder = function(order, callback) {

  var get = function(result, err) {

    this.logAction('getOrder', 'called');

    if(err)
      return callback(err);

    var price = 0;
    var amount = 0;
    var date = moment(0);

    if(!result.success)
      return callback(null, {price, amount, date});

    var resultOrder = result.result;

    price = resultOrder.Price;
    amount = resultOrder.Quantity;

    if(resultOrder.IsOpen) {
         date = moment(resultOrder.Opened);
    } else {
       date = moment(resultOrder.Closed);
    }

    this.logAction('getOrder', 'result', {price, amount, date});
    callback(err, {price, amount, date});
  }.bind(this);

  this.bittrexApi.getorder({uuid: order}, get);
}

Trader.prototype.cancelOrder = function(order, callback) {
  var args = _.toArray(arguments);
  var cancel = function(result, err) {
    this.logAction('cancelOrder', 'called', order);

    if(err || !result.success) {
      log.error('unable to cancel order', order, '(', err, result, '), retrying');
      return this.retry(this.cancelOrder, args);
    }

    this.logAction('getOrder', 'result', result);

    callback();
  }.bind(this);

  this.bittrexApi.cancel({uuid: order}, cancel);
}

Trader.prototype.getTrades = function(since, callback, descending) {

  var firstFetch = !!since;

  var args = _.toArray(arguments);
  var process = function(data, err) {
    if(err) {
      return this.retry(this.getTrades, args);
    }

    let result = data.result;

    // Edge case, see here:
    // @link https://github.com/askmike/gekko/issues/479
    if(firstFetch && _.size(result) === 50000)
      util.die(
        [
          'Poloniex did not provide enough data. Read this:',
          'https://github.com/askmike/gekko/issues/479'
        ].join('\n\n')
      );

      result = _.map(result, function(trade) {
        let mr = {
            tid: trade.Id,
            amount: +trade.Quantity,
            date: moment.utc(trade.TimeStamp).unix(),
            timeStamp: trade.TimeStamp,
            price: +trade.Price
        };
    	return mr;
    });

    callback(null, result.reverse());
  }.bind(this);

  var params = {
    currencyPair: joinCurrencies(this.currency, this.asset)
  }

  if(since)
    params.start = since.unix();

  this.bittrexApi.getmarkethistory({ market: params.currencyPair }, process);
}

Trader.getCapabilities = function () {
  return {
    name: 'bittrex',
    slug: 'bittrex',
    currencies: ['BTC', 'ETH', 'USDT'],
    assets: [
      'BTC', 'BCC','ETH','ANS','BCC'
    ],
    markets: [
      // *** BTC <-> XXX
      { pair: ['BTC', 'BCC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['BTC', 'ANS'], minimalOrder: { amount: 0.0001, unit: 'asset' } },

      // *** USDT <-> XXX
      { pair: ['USDT', 'BTC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['USDT', 'BCC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['USDT', 'ETH'], minimalOrder: { amount: 0.0001, unit: 'asset' } },

      // *** ETH <-> XXX
      { pair: ['ETH', 'BCC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
      { pair: ['ETH', 'ANS'], minimalOrder: { amount: 0.0001, unit: 'asset' } }

    ],
    requires: ['key', 'secret'],
    tid: 'tid',
    providesHistory: 'date',
    providesFullHistory: true,
    tradable: true
  };
}

Trader.prototype.logAction = function(method, msg, obj) {
  console.info(this.name +' ' + method + ': ' + msg);
  if(_.isObject(obj)) {
    console.info(obj);
  }
}

module.exports = Trader;
<|endoftext|>


<commit_before><commit_msg>Fix removing bad fields in `update`<commit_after><|endoftext|>


<commit_before>
/*

  Compile.js

*/

var _           = require("lodash"),
    Utils       = require("./utils"),
    Bundler     = require("./bundler"),
    Promise     = require("bluebird"),
    express     = require("express"),
    Metadata    = require("./metadata"),
    Motors      = global.engines,
    compileFile = _.curry(_.bind(Motors.compileFile, Motors));

function makeDataQuery(queries, context) {

  if (queries) {

    var key,
        keys = _.keys(context),
        re   = new RegExp("<(" + keys.join("|") + ")>", "gi"),
        match;

    queries = _.cloneDeep(queries);

    queries.forEach(function(query) {
      for (key in query.filters || {}) {
        if (_.isString(query.filters[key]) && (match = query.filters[key].match(re))) {
          match = match[0];
          query.filters[key] = query.filters[key].replace(new RegExp(match, "g"), context[match.slice(1, match.length-1)]);
        }
      }
    });

    return function() { return global.db.queries(queries); }
  } else
    return _.constant({});

}

function addFile(router, route, file, type, basePath) {

    var ext = file.ext.replace(/^\./, ""),
        handler,
        compiler,
        fetcher,
        metadata = {};

    // Add to list of extensions if not present
    if (!Motors.hasEngine(ext)) Motors.addEngine(ext);

    // Extract metadata
    metadata = ("html" === type) ? Metadata.extract(Utils.readFileHead(file.path, 500)) : {};

    // Data fetching function
    if ("html" === type) {
      fetcher = function(context, next) {
        next(null, _.assign({}, global.vars, context, makeDataQuery(metadata.data, context)()));
      };
    } else
      fetcher = function(context, next) { next(null, {}) };

    // File compilation function
    if (file.bundle) {
      file.folder = "";
      file.name = file.dir.replace(Utils.makePathAbsolute(basePath), "").slice(1);
      bundler = new Bundler({ entry : file.path, filename : file.name + "." + type });
      compiler = function(data, next) { return bundler.get(next); }
    } else
      compiler = function(data, next) { Motors.compileFile(file.path, data, next); };

    /*
    // Set cookies
    for (var key in metadata.cookies || {})
      res.cookie(key, metadata.cookies[key]);

    // Set session
    for (var key in metadata.session || {})
      req.session[key] = metadata.session[key];
    */

    // Route handling function
    handler = function(req, res) {
      var context = _.assign({}, req.params);
      fetcher(context, function(err, data) {
        if (err) return res.sendStatus(500);
        compiler(data, function(err, compiled) {
          if (err) return res.sendStatus(500);
          res.type(type).send(compiled);
        });
      });
    };

    // Add routes to router
    Utils.makeRoutes(route, file, { type : type, query : metadata.query, bundle : file.bundle }).forEach(function(r) {
      router.get(r, handler);
    });

}

module.exports = function(options) {

  var router = express(),
      files = Utils.mapFiles(options.path, options);

  files.forEach(function(file) { addFile(router, options.route, file, options.ext, options.path) });

  // Set up Browsersync
  _(files).filter(function(x) { return !x.bundle }).forEach(function(file) {
    var ext = file.ext.replace(/^\./, "");
    if (!Motors.hasEngine(ext)) Motors.addEngine(ext);
    Utils.watch(options.path, ext, options.ext);
  });

  return router;

};

<commit_msg>compiler: adds #sequence helper<commit_after>
/*

  Compile.js

*/

var _           = require("lodash"),
    Utils       = require("./utils"),
    Bundler     = require("./bundler"),
    Promise     = require("bluebird"),
    express     = require("express"),
    Metadata    = require("./metadata"),
    Motors      = global.engines,
    compileFile = _.curry(_.bind(Motors.compileFile, Motors));

function makeDataQuery(queries, context) {

  if (queries) {

    var key,
        keys = _.keys(context),
        re   = new RegExp("<(" + keys.join("|") + ")>", "gi"),
        match;

    queries = _.cloneDeep(queries);

    queries.forEach(function(query) {
      for (key in query.filters || {}) {
        if (_.isString(query.filters[key]) && (match = query.filters[key].match(re))) {
          match = match[0];
          query.filters[key] = query.filters[key].replace(new RegExp(match, "g"), context[match.slice(1, match.length-1)]);
        }
      }
    });

    return function() { return global.db.queries(queries); }
  } else
    return _.constant({});

function sequence(tasks, initial) {
  return Promise.reduce(tasks || [], function(val, task) {
    return task(val);
  }, initial);
}

function addFile(router, route, file, type, basePath) {

    var ext = file.ext.replace(/^\./, ""),
        handler,
        compiler,
        fetcher,
        metadata = {};

    // Add to list of extensions if not present
    if (!Motors.hasEngine(ext)) Motors.addEngine(ext);

    // Extract metadata
    metadata = ("html" === type) ? Metadata.extract(Utils.readFileHead(file.path, 500)) : {};

    // Data fetching function
    if ("html" === type) {
      fetcher = function(context, next) {
        next(null, _.assign({}, global.vars, context, makeDataQuery(metadata.data, context)()));
      };
    } else
      fetcher = function(context, next) { next(null, {}) };

    // File compilation function
    if (file.bundle) {
      file.folder = "";
      file.name = file.dir.replace(Utils.makePathAbsolute(basePath), "").slice(1);
      bundler = new Bundler({ entry : file.path, filename : file.name + "." + type });
      compiler = function(data, next) { return bundler.get(next); }
    } else
      compiler = function(data, next) { Motors.compileFile(file.path, data, next); };

    /*
    // Set cookies
    for (var key in metadata.cookies || {})
      res.cookie(key, metadata.cookies[key]);

    // Set session
    for (var key in metadata.session || {})
      req.session[key] = metadata.session[key];
    */

    // Route handling function
    handler = function(req, res) {
      var context = _.assign({}, req.params);
      fetcher(context, function(err, data) {
        if (err) return res.sendStatus(500);
        compiler(data, function(err, compiled) {
          if (err) return res.sendStatus(500);
          res.type(type).send(compiled);
        });
      });
    };

    // Add routes to router
    Utils.makeRoutes(route, file, { type : type, query : metadata.query, bundle : file.bundle }).forEach(function(r) {
      router.get(r, handler);
    });

}

module.exports = function(options) {

  var router = express(),
      files = Utils.mapFiles(options.path, options);

  files.forEach(function(file) { addFile(router, options.route, file, options.ext, options.path) });

  // Set up Browsersync
  _(files).filter(function(x) { return !x.bundle }).forEach(function(file) {
    var ext = file.ext.replace(/^\./, "");
    if (!Motors.hasEngine(ext)) Motors.addEngine(ext);
    Utils.watch(options.path, ext, options.ext);
  });

  return router;

};

<|endoftext|>


<commit_before>/**
 * Autor: John P. Masseria (john@masseria.org)
 */

// Limit concurrent camera access
var sem = require('semaphore')(1);
var uuid = require('uuid');

var port = 8080;
var
    app = require('http').createServer(handler).listen(port, "0.0.0.0"),
    path = require('path'),
    fs = require('fs'),
    sys = require('util'),
    exec = require('child_process').exec,
    child, child1;

function handler(req, res) {
    var
        fileName = path.basename(req.url) || 'index.html',
        ext = path.extname(fileName),
        localFolder = __dirname + '/',
        //localFolder = __dirname + '/public/',
        page404 = localFolder + '404.html';

    //do we support the requested file type?
    if(!extensions[ext]){
        //for now just send a 404 and a short message
        res.writeHead(404, {'Content-Type': 'text/html'});
        res.end("The requested file type is not supported");
    };

    if( fileName.toLowerCase() == 'snap.jpg' ) {
        sem.take(function() {
            var fname = uuid.v4(); // Get a random filename
            child = exec('../uvccapture/uvccapture -o/tmp/' + fname + ' -x640 -y480 -j5 -m -c./decorate.sh', function (error, stdout, stderr) {
                if (error !== null) {
                    console.log('kernel exec error: ' + error);
                } else {
                    // Use synchronous I/O to read image
                    getFileSync(('/tmp/' + fname + '.jpg'), res, extensions['jpg']);
                };
                child = exec('rm /tmp/' + fname + '*', function (error, stdout, stderr) {
                    if (error !== null)
                        console.log('rm error: ' + error);
                    sem.leave();
                });
            });
        });
    } else {
        //call our helper function
        //pass in the path to the file we want,
        //the response object, and the 404 page path
        //in case the requestd file is not found
        getFile((localFolder + fileName), res, page404, extensions[ext]);
    };
};

//these are the only file types we will support
extensions = {
    ".html": "text/html",
    ".css" : "text/css",
    ".js"  : "application/javascript",
    ".png" : "image/png",
    ".gif" : "image/gif",
    ".jpg" : "image/jpeg",
    ".ico" : "image/x-icon"
};

//helper function handles file verification
function getFileSync(filePath, res, mimeType){
    //read the file using synchronous I/O
    var contents = fs.readFileSync(filePath);
    //send the contents with the default 200/ok header
    res.writeHead(200,{
        "Content-type" : mimeType,
        "Content-Length" : contents.length
    });
    res.end(contents);
};

//helper function handles file verification
function getFile(filePath, res, page404, mimeType){
    //does the requested file exist?
    fs.exists(filePath, function(exists){
        if(exists){
            //read the file, run the anonymous function
            fs.readFile(filePath, function(err,contents){
                if(!err){
                    //send the contents with the default 200/ok header
                    res.writeHead(200,{
                        "Content-type" : mimeType,
                        "Content-Length" : contents.length
                    });
                    res.end(contents);
                } else
                    console.dir('getFile: ' + err);  //for our own troubleshooting
            });
        } else {
            //if the requested file was not found serve-up our custom 404 page
            fs.readFile(page404, function(err,contents){
                if(!err){
                    //send the contents with a 404/not found header
                    res.writeHead(404, {'Content-Type': 'text/html'});
                    res.end(contents);
                } else
                    console.dir('getFile: ' + err);  //for our own troubleshooting
            });
        };
    });
};

// listen for an HTTP request on port specified
app.listen(port);
<commit_msg>Use absolute path to uvccapture and output exec stdout, stderr to console.log<commit_after>/**
 * Autor: John P. Masseria (john@masseria.org)
 */

// Limit concurrent camera access
var sem = require('semaphore')(1);
var uuid = require('uuid');

var port = 8080;
var
    app = require('http').createServer(handler).listen(port, "0.0.0.0"),
    path = require('path'),
    fs = require('fs'),
    sys = require('util'),
    exec = require('child_process').exec,
    child, child1;

function handler(req, res) {
    var
        fileName = path.basename(req.url) || 'index.html',
        ext = path.extname(fileName),
        localFolder = __dirname + '/',
        //localFolder = __dirname + '/public/',
        page404 = localFolder + '404.html';

    //do we support the requested file type?
    if(!extensions[ext]){
        //for now just send a 404 and a short message
        res.writeHead(404, {'Content-Type': 'text/html'});
        res.end("The requested file type is not supported");
    };

    if( fileName.toLowerCase() == 'snap.jpg' ) {
        sem.take(function() {
            var fname = uuid.v4(); // Get a random filename
            child = exec('/home/chip/uvccapture/uvccapture -o/tmp/' + fname + ' -x1280 -y720 -j2 -vvv -m -c./decorate.sh', function (error, stdout, stderr) {
                if (error !== null) {
                    console.log('kernel exec error: ' + error);
                } else {
                    console.log('exec stdout: ' + stdout);
                    console.log('exec stderr: ' + stderr);
                    // Use synchronous I/O to read image
                    getFileSync(('/tmp/' + fname + '.jpg'), res, extensions['jpg']);
                };
                child = exec('rm /tmp/' + fname + '*', function (error, stdout, stderr) {
                    if (error !== null)
                        console.log('rm error: ' + error);
                    sem.leave();
                });
            });
        });
    } else {
        //call our helper function
        //pass in the path to the file we want,
        //the response object, and the 404 page path
        //in case the requestd file is not found
        getFile((localFolder + fileName), res, page404, extensions[ext]);
    };
};

//these are the only file types we will support
extensions = {
    ".html": "text/html",
    ".css" : "text/css",
    ".js"  : "application/javascript",
    ".png" : "image/png",
    ".gif" : "image/gif",
    ".jpg" : "image/jpeg",
    ".ico" : "image/x-icon"
};

//helper function handles file verification
function getFileSync(filePath, res, mimeType){
    //read the file using synchronous I/O
    var contents = fs.readFileSync(filePath);
    //send the contents with the default 200/ok header
    res.writeHead(200,{
        "Content-type" : mimeType,
        "Content-Length" : contents.length
    });
    res.end(contents);
};

//helper function handles file verification
function getFile(filePath, res, page404, mimeType){
    //does the requested file exist?
    fs.exists(filePath, function(exists){
        if(exists){
            //read the file, run the anonymous function
            fs.readFile(filePath, function(err,contents){
                if(!err){
                    //send the contents with the default 200/ok header
                    res.writeHead(200,{
                        "Content-type" : mimeType,
                        "Content-Length" : contents.length
                    });
                    res.end(contents);
                } else
                    console.dir('getFile: ' + err);  //for our own troubleshooting
            });
        } else {
            //if the requested file was not found serve-up our custom 404 page
            fs.readFile(page404, function(err,contents){
                if(!err){
                    //send the contents with a 404/not found header
                    res.writeHead(404, {'Content-Type': 'text/html'});
                    res.end(contents);
                } else
                    console.dir('getFile: ' + err);  //for our own troubleshooting
            });
        };
    });
};

// listen for an HTTP request on port specified
app.listen(port);
<|endoftext|>


<commit_before>import _ from 'lodash';
import fsp from 'fs-promise';
import mongoose from 'mongoose';
import async from 'async';
import objectId from 'libs/objectId';
import errors from 'libs/errors';

export default () => {
  const MatchSchema = new mongoose.Schema({
    status: String,
    u1: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    u2: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    u1Submission: { type: mongoose.Schema.Types.ObjectId, ref: 'Submission' },
    u2Submission: { type: mongoose.Schema.Types.ObjectId, ref: 'Submission' },
    u1Stat: { // only exists after match is completed
      score: Number,
      win: Number,
      lose: Number,
      draw: Number,
    },
    u2Stat: { // only exists after match is completed
      score: Number,
      win: Number,
      lose: Number,
      draw: Number,
    },
    rounds: [{
      _id: mongoose.Schema.Types.ObjectId,
      status: String,
      u1Black: Boolean,
      u2Black: Boolean, // !u1Black
      openingId: String,
      beginJudgeAt: Date,
      endJudgeAt: Date,
      logBlob: mongoose.Schema.Types.ObjectId,  // grid fs
      text: String,
      summary: String,
    }],
  }, {
    timestamps: true,
  });

  // Match Model
  let Match;

  // For both match and round
  MatchSchema.statics.STATUS_PENDING = 'pending';
  MatchSchema.statics.STATUS_RUNNING = 'running';
  MatchSchema.statics.STATUS_SYSTEM_ERROR = 'se';
  MatchSchema.statics.STATUS_U1WIN = 'u1win';
  MatchSchema.statics.STATUS_U2WIN = 'u2win';
  MatchSchema.statics.STATUS_DRAW = 'draw';

  MatchSchema.statics.RELATIVE_STATUS_WIN = 'win';
  MatchSchema.statics.RELATIVE_STATUS_LOSE = 'lose';
  MatchSchema.statics.RELATIVE_STATUS_DRAW = 'draw';

  MatchSchema.statics.JUDGE_EXITCODE_MIN = 33;

  MatchSchema.statics.JUDGE_EXITCODE_STATUS = {
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 0]: MatchSchema.statics.STATUS_U1WIN,
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 1]: MatchSchema.statics.STATUS_U2WIN,
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 2]: MatchSchema.statics.STATUS_DRAW,
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 3]: MatchSchema.statics.STATUS_SYSTEM_ERROR,
  };

  MatchSchema.statics.STATUS_TEXT = {
    'pending': 'Pending',
    'running': 'Running',
    'se': 'Error',
    'win': 'Win',
    'lose': 'Lose',
    'u1win': 'Challenger Win',
    'u2win': 'Challenger Lose',
    'draw': 'Draw',
  };

  MatchSchema.statics.ROUND_STATUS_TEXT = MatchSchema.statics.STATUS_TEXT;

  async function updateSingleMatchStatus(matchId) {
    try {
      const match = await Match.getMatchObjectByIdAsync(matchId);
      match.updateMatchStatus();
      await match.save();
    } catch (ignored) {
      // ignore errors
    }
  }

  /**
   * Single worker queue to deal with match status updates to avoid race conditions
   */
  const matchStatusUpdateQueue = async.queue((matchId, callback) => {
    updateSingleMatchStatus(matchId).then(() => callback());
  }, 1);

  /**
   * Update the match status when round status is updated
   */
  MatchSchema.pre('save', function (next) {
    const modifiedPaths = this.modifiedPaths();
    if (_.some(modifiedPaths, path => path.match(/^rounds\.\d+\.status$/))) {
      matchStatusUpdateQueue.push(this._id);
    }
    next();
  });

  /**
   * Broadcast match status changed event when it is changed
   */
  MatchSchema.pre('save', function (next) {
    if (this.isModified('status')) {
      setTimeout(() => DI.eventBus.emit('match.statusChanged', this), 1000);
    }
    next();
  });

  /**
   * Determine whether a match status is one of running status (pending / running)
   *
   * @param  {String}  matchStatus
   * @return {Boolean}
   */
  MatchSchema.statics.isRunningStatus = function (matchStatus) {
    if (matchStatus === Match.STATUS_PENDING) {
      return true;
    }
    if (matchStatus === Match.STATUS_RUNNING) {
      return true;
    }
    return false;
  };

  /**
   * Get the match object by userId
   *
   * @return {Match} Mongoose match object
   */
  MatchSchema.statics.getMatchObjectByIdAsync = async function (id, projection = {}, throwWhenNotFound = true) {
    if (!objectId.isValid(id)) {
      if (throwWhenNotFound) {
        throw new errors.UserError('Match not found');
      } else {
        return null;
      }
    }
    const s = await Match.findOne({ _id: id }, projection).exec();
    if (s === null && throwWhenNotFound) {
      throw new errors.UserError('Match not found');
    }
    return s;
  };

  /**
   * Get round status from fun-judge exit code
   * @param  {Number} exitCode
   * @return {String}
   */
  MatchSchema.statics.getStatusFromJudgeExitCode = function (exitCode) {
    if (Match.JUDGE_EXITCODE_STATUS[exitCode] !== undefined) {
      return Match.JUDGE_EXITCODE_STATUS[exitCode];
    }
    return Match.STATUS_SYSTEM_ERROR;
  };

  /**
   * Get opening data from opening id
   * @param  {String} openingId
   * @return {String}
   */
  MatchSchema.statics.getOpeningFromIdAsync = async function (openingId) {
    const filePath = `./openings/${openingId}.json`;
    const content = await fsp.readFile(filePath);
    return content.toString();
  };

  /**
   * Generate initial round docs for a match
   * @return {[Match.Round]}
   */
  function generateRoundDocs() {
    const rounds = [];
    for (const openingId of DI.config.match.openings) {
      for (const u1Black of [true, false]) {
        rounds.push({
          _id: mongoose.Types.ObjectId(),
          status: Match.STATUS_PENDING,
          u1Black,
          u2Black: !u1Black,
          openingId: String(openingId),
        });
      }
    }
    return rounds;
  }

  /**
   * Add all matches for a new submission. If the submission have related matches
   * previously, they will be removed.
   *
   * @param {Submission} s1
   * @param {User} u1
   * @param {[{_id: User, sdocid: Submission}]} s2u2docs
   */
  MatchSchema.statics.addMatchesForSubmissionAsync = async function (s1, u1, s2u2docs) {
    if (s2u2docs.length === 0) {
      return [];
    }
    await Match.remove({ u1Submission: s1 });
    const mdocs = await Match.insertMany(s2u2docs.map(s2u2doc => ({
      status: Match.STATUS_PENDING,
      u1,
      u2: s2u2doc._id,
      u1Submission: s1,
      u2Submission: s2u2doc.sdocid,
      rounds: generateRoundDocs(),
    })));
    // push each round of each match into the queue
    for (const match of mdocs) {
      for (const round of match.rounds) {
        await DI.mq.publish('judge', {
          mdocid: String(match._id),
          s1docid: String(match.u1Submission),
          s2docid: String(match.u2Submission),
          rid: round._id,
          u1field: round.u1Black ? 'black' : 'white',
          opening: await Match.getOpeningFromIdAsync(round.openingId),
          rules: DI.config.match.rules,
        });
      }
    }
    return mdocs;
  };

  /**
   * Translate MatchStatus into RelativeStatus
   *
   * @param  {String}  status
   * @param  {Boolean} isU1
   * @return {String}
   */
  MatchSchema.statics.getRelativeStatus = function (status, isU1 = true) {
    if (status !== Match.STATUS_U1WIN && status !== Match.STATUS_U2WIN) {
      return status;
    }
    if (isU1) {
      if (status === Match.STATUS_U1WIN) {
        return Match.RELATIVE_STATUS_WIN;
      } else {
        return Match.RELATIVE_STATUS_LOSE;
      }
    } else {
      if (status === Match.STATUS_U1WIN) {
        return Match.RELATIVE_STATUS_LOSE;
      } else {
        return Match.RELATIVE_STATUS_WIN;
      }
    }
  };

  /**
   * Get score according to wins and draws
   */
  function getScore(win, draw) {
    return win * 3 + draw * 1;
  }

  /**
   * Update match status according to round status
   */
  MatchSchema.methods.updateMatchStatus = function () {
    const statusStat = {
      [Match.STATUS_PENDING]: 0,
      [Match.STATUS_RUNNING]: 0,
      [Match.STATUS_SYSTEM_ERROR]: 0,
      [Match.STATUS_U1WIN]: 0,
      [Match.STATUS_U2WIN]: 0,
      [Match.STATUS_DRAW]: 0,
    };
    this.rounds.forEach(rdoc => statusStat[rdoc.status]++);
    if (statusStat[Match.STATUS_PENDING] === this.rounds.length) {
      // all pending
      this.status = Match.STATUS_PENDING;
      return;
    }
    if (statusStat[Match.STATUS_SYSTEM_ERROR] > 0) {
      // some system error
      this.status = Match.STATUS_SYSTEM_ERROR;
      return;
    }
    if (statusStat[Match.STATUS_RUNNING] > 0 || statusStat[Match.STATUS_PENDING] > 0) {
      // some pending, or some running
      this.status = Match.STATUS_RUNNING;
      return;
    }
    if (statusStat[Match.STATUS_U1WIN] > statusStat[Match.STATUS_U2WIN]) {
      this.status = Match.STATUS_U1WIN;
    } else if (statusStat[Match.STATUS_U1WIN] < statusStat[Match.STATUS_U2WIN]) {
      this.status = Match.STATUS_U2WIN;
    } else {
      this.status = Match.STATUS_DRAW;
    }
    // Update u1Stat and u2Stat
    this.u1Stat = {
      win: statusStat[Match.STATUS_U1WIN],
      lose: statusStat[Match.STATUS_U2WIN],
      draw: statusStat[Match.STATUS_DRAW],
    };
    this.u1Stat.score = getScore(this.u1Stat.win, this.u1Stat.draw);
    this.u2Stat = {
      win: statusStat[Match.STATUS_U2WIN],
      lose: statusStat[Match.STATUS_U1WIN],
      draw: statusStat[Match.STATUS_DRAW],
    };
    this.u2Stat.score = getScore(this.u2Stat.win, this.u2Stat.draw);
  };

  /**
   * Mark a pending round as running
   *
   * @param  {MongoId} mdocid Match Id
   * @param  {MongoId} rid Round Id
   * @return {Match}
   */
  MatchSchema.statics.judgeStartRoundAsync = async function (mdocid, rid) {
    const mdoc = await Match.getMatchObjectByIdAsync(mdocid);
    const round = mdoc.rounds.find(rdoc => rdoc._id.equals(rid));
    if (round !== undefined) {
      round.status = Match.STATUS_RUNNING;
      round.beginJudgeAt = new Date();
      await mdoc.save();
    }
    return mdoc;
  };

  /**
   * Mark a round as completed (u1win, u2win or draw) or error.
   *
   * @param  {MongoId} mdocid Match Id
   * @param  {MongoId} rid Round Id
   * @param  {String} status
   * @param  {Object} extra Extra fields to be added to the round
   * @return {Match}
   */
  MatchSchema.statics.judgeCompleteRoundAsync = async function (mdocid, rid, status, extra = {}) {
    if (
      status !== Match.STATUS_U1WIN
      && status !== Match.STATUS_U2WIN
      && status !== Match.STATUS_DRAW
      && status !== Match.STATUS_SYSTEM_ERROR
    ) {
      throw new Error(`judgeCompleteRoundAsync: status ${status} is invalid`);
    }
    const mdoc = await Match.getMatchObjectByIdAsync(mdocid);
    const round = mdoc.rounds.find(rdoc => rdoc._id.equals(rid));
    if (round !== undefined) {
      round.status = status;
      round.endJudgeAt = new Date();
      if (!round.beginJudgeAt) {
        round.beginJudgeAt = new Date();
      }
      _.assign(round, extra);
      await mdoc.save();
    }
    return mdoc;
  };

  /**
   * Get all related matches for a submission
   * @param  {MongoId} sid
   * @return {[Match]}
   */
  MatchSchema.statics.getMatchesForSubmissionAsync = async function (sid) {
    const mdocs = await Match.find({
      $or: [
        { u1Submission: sid },
        { u2Submission: sid },
      ],
    }).sort({ _id: -1 }).limit(20);
    return mdocs;
  };

  /**
   * Update status of all matches
   */
  MatchSchema.statics.refreshAllMatchesAsync = async function () {
    let ret = {
      updated: 0,
      all: 0,
    };
    const cursor = Match.find().sort({ _id: 1 }).cursor();
    for (let mdoc = await cursor.next(); mdoc !== null; mdoc = await cursor.next()) {
      ret.all++;
      mdoc.updateMatchStatus();
      if (mdoc.isModified('status')) {
        ret.updated++;
        await mdoc.save();
      }
    }
    return ret;
  };

  /**
   * Given a list of allowed submissions _id, return all useable matches.
   * @return { [MatchModel] }
   * @param allowedSubmissionIds { [ObjectId] }
   */
  MatchSchema.statics.getPairwiseMatchesForSubmissionsAsync = async function(allowedSubmissionIds) {
    return await this
      .find({
        u1Submission: {
          $in: allowedSubmissionIds,
        },
        u2Submission: {
          $in: allowedSubmissionIds,
        },
      })
      .exec();
  };

  MatchSchema.index({ u1Submission: 1, u2Submission: -1 }, { unique: true });
  MatchSchema.index({ u2Submission: 1 });

  Match = mongoose.model('Match', MatchSchema);
  return Match;

};
<commit_msg>fix incorrect score<commit_after>import _ from 'lodash';
import fsp from 'fs-promise';
import mongoose from 'mongoose';
import async from 'async';
import objectId from 'libs/objectId';
import errors from 'libs/errors';

export default () => {
  const MatchSchema = new mongoose.Schema({
    status: String,
    u1: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    u2: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    u1Submission: { type: mongoose.Schema.Types.ObjectId, ref: 'Submission' },
    u2Submission: { type: mongoose.Schema.Types.ObjectId, ref: 'Submission' },
    u1Stat: { // only exists after match is completed
      score: Number,
      win: Number,
      lose: Number,
      draw: Number,
    },
    u2Stat: { // only exists after match is completed
      score: Number,
      win: Number,
      lose: Number,
      draw: Number,
    },
    rounds: [{
      _id: mongoose.Schema.Types.ObjectId,
      status: String,
      u1Black: Boolean,
      u2Black: Boolean, // !u1Black
      openingId: String,
      beginJudgeAt: Date,
      endJudgeAt: Date,
      logBlob: mongoose.Schema.Types.ObjectId,  // grid fs
      text: String,
      summary: String,
    }],
  }, {
    timestamps: true,
  });

  // Match Model
  let Match;

  // For both match and round
  MatchSchema.statics.STATUS_PENDING = 'pending';
  MatchSchema.statics.STATUS_RUNNING = 'running';
  MatchSchema.statics.STATUS_SYSTEM_ERROR = 'se';
  MatchSchema.statics.STATUS_U1WIN = 'u1win';
  MatchSchema.statics.STATUS_U2WIN = 'u2win';
  MatchSchema.statics.STATUS_DRAW = 'draw';

  MatchSchema.statics.RELATIVE_STATUS_WIN = 'win';
  MatchSchema.statics.RELATIVE_STATUS_LOSE = 'lose';
  MatchSchema.statics.RELATIVE_STATUS_DRAW = 'draw';

  MatchSchema.statics.JUDGE_EXITCODE_MIN = 33;

  MatchSchema.statics.JUDGE_EXITCODE_STATUS = {
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 0]: MatchSchema.statics.STATUS_U1WIN,
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 1]: MatchSchema.statics.STATUS_U2WIN,
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 2]: MatchSchema.statics.STATUS_DRAW,
    [MatchSchema.statics.JUDGE_EXITCODE_MIN + 3]: MatchSchema.statics.STATUS_SYSTEM_ERROR,
  };

  MatchSchema.statics.STATUS_TEXT = {
    'pending': 'Pending',
    'running': 'Running',
    'se': 'Error',
    'win': 'Win',
    'lose': 'Lose',
    'u1win': 'Challenger Win',
    'u2win': 'Challenger Lose',
    'draw': 'Draw',
  };

  MatchSchema.statics.ROUND_STATUS_TEXT = MatchSchema.statics.STATUS_TEXT;

  async function updateSingleMatchStatus(matchId) {
    try {
      const match = await Match.getMatchObjectByIdAsync(matchId);
      match.updateMatchStatus();
      await match.save();
    } catch (ignored) {
      // ignore errors
    }
  }

  /**
   * Single worker queue to deal with match status updates to avoid race conditions
   */
  const matchStatusUpdateQueue = async.queue((matchId, callback) => {
    updateSingleMatchStatus(matchId).then(() => callback());
  }, 1);

  /**
   * Update the match status when round status is updated
   */
  MatchSchema.pre('save', function (next) {
    const modifiedPaths = this.modifiedPaths();
    if (_.some(modifiedPaths, path => path.match(/^rounds\.\d+\.status$/))) {
      matchStatusUpdateQueue.push(this._id);
    }
    next();
  });

  /**
   * Broadcast match status changed event when it is changed
   */
  MatchSchema.pre('save', function (next) {
    if (this.isModified('status')) {
      setTimeout(() => DI.eventBus.emit('match.statusChanged', this), 1000);
    }
    next();
  });

  /**
   * Determine whether a match status is one of running status (pending / running)
   *
   * @param  {String}  matchStatus
   * @return {Boolean}
   */
  MatchSchema.statics.isRunningStatus = function (matchStatus) {
    if (matchStatus === Match.STATUS_PENDING) {
      return true;
    }
    if (matchStatus === Match.STATUS_RUNNING) {
      return true;
    }
    return false;
  };

  /**
   * Get the match object by userId
   *
   * @return {Match} Mongoose match object
   */
  MatchSchema.statics.getMatchObjectByIdAsync = async function (id, projection = {}, throwWhenNotFound = true) {
    if (!objectId.isValid(id)) {
      if (throwWhenNotFound) {
        throw new errors.UserError('Match not found');
      } else {
        return null;
      }
    }
    const s = await Match.findOne({ _id: id }, projection).exec();
    if (s === null && throwWhenNotFound) {
      throw new errors.UserError('Match not found');
    }
    return s;
  };

  /**
   * Get round status from fun-judge exit code
   * @param  {Number} exitCode
   * @return {String}
   */
  MatchSchema.statics.getStatusFromJudgeExitCode = function (exitCode) {
    if (Match.JUDGE_EXITCODE_STATUS[exitCode] !== undefined) {
      return Match.JUDGE_EXITCODE_STATUS[exitCode];
    }
    return Match.STATUS_SYSTEM_ERROR;
  };

  /**
   * Get opening data from opening id
   * @param  {String} openingId
   * @return {String}
   */
  MatchSchema.statics.getOpeningFromIdAsync = async function (openingId) {
    const filePath = `./openings/${openingId}.json`;
    const content = await fsp.readFile(filePath);
    return content.toString();
  };

  /**
   * Generate initial round docs for a match
   * @return {[Match.Round]}
   */
  function generateRoundDocs() {
    const rounds = [];
    for (const openingId of DI.config.match.openings) {
      for (const u1Black of [true, false]) {
        rounds.push({
          _id: mongoose.Types.ObjectId(),
          status: Match.STATUS_PENDING,
          u1Black,
          u2Black: !u1Black,
          openingId: String(openingId),
        });
      }
    }
    return rounds;
  }

  /**
   * Add all matches for a new submission. If the submission have related matches
   * previously, they will be removed.
   *
   * @param {Submission} s1
   * @param {User} u1
   * @param {[{_id: User, sdocid: Submission}]} s2u2docs
   */
  MatchSchema.statics.addMatchesForSubmissionAsync = async function (s1, u1, s2u2docs) {
    if (s2u2docs.length === 0) {
      return [];
    }
    await Match.remove({ u1Submission: s1 });
    const mdocs = await Match.insertMany(s2u2docs.map(s2u2doc => ({
      status: Match.STATUS_PENDING,
      u1,
      u2: s2u2doc._id,
      u1Submission: s1,
      u2Submission: s2u2doc.sdocid,
      rounds: generateRoundDocs(),
    })));
    // push each round of each match into the queue
    for (const match of mdocs) {
      for (const round of match.rounds) {
        await DI.mq.publish('judge', {
          mdocid: String(match._id),
          s1docid: String(match.u1Submission),
          s2docid: String(match.u2Submission),
          rid: round._id,
          u1field: round.u1Black ? 'black' : 'white',
          opening: await Match.getOpeningFromIdAsync(round.openingId),
          rules: DI.config.match.rules,
        });
      }
    }
    return mdocs;
  };

  /**
   * Translate MatchStatus into RelativeStatus
   *
   * @param  {String}  status
   * @param  {Boolean} isU1
   * @return {String}
   */
  MatchSchema.statics.getRelativeStatus = function (status, isU1 = true) {
    if (status !== Match.STATUS_U1WIN && status !== Match.STATUS_U2WIN) {
      return status;
    }
    if (isU1) {
      if (status === Match.STATUS_U1WIN) {
        return Match.RELATIVE_STATUS_WIN;
      } else {
        return Match.RELATIVE_STATUS_LOSE;
      }
    } else {
      if (status === Match.STATUS_U1WIN) {
        return Match.RELATIVE_STATUS_LOSE;
      } else {
        return Match.RELATIVE_STATUS_WIN;
      }
    }
  };

  /**
   * Update match status according to round status
   */
  MatchSchema.methods.updateMatchStatus = function () {
    const statusStat = {
      [Match.STATUS_PENDING]: 0,
      [Match.STATUS_RUNNING]: 0,
      [Match.STATUS_SYSTEM_ERROR]: 0,
      [Match.STATUS_U1WIN]: 0,
      [Match.STATUS_U2WIN]: 0,
      [Match.STATUS_DRAW]: 0,
    };
    this.rounds.forEach(rdoc => statusStat[rdoc.status]++);
    if (statusStat[Match.STATUS_PENDING] === this.rounds.length) {
      // all pending
      this.status = Match.STATUS_PENDING;
      return;
    }
    if (statusStat[Match.STATUS_SYSTEM_ERROR] > 0) {
      // some system error
      this.status = Match.STATUS_SYSTEM_ERROR;
      return;
    }
    if (statusStat[Match.STATUS_RUNNING] > 0 || statusStat[Match.STATUS_PENDING] > 0) {
      // some pending, or some running
      this.status = Match.STATUS_RUNNING;
      return;
    }
    // Update u1Stat and u2Stat
    this.u1Stat = {
      win: statusStat[Match.STATUS_U1WIN],
      lose: statusStat[Match.STATUS_U2WIN],
      draw: statusStat[Match.STATUS_DRAW],
      score: 0,
    };
    this.u2Stat = {
      win: statusStat[Match.STATUS_U2WIN],
      lose: statusStat[Match.STATUS_U1WIN],
      draw: statusStat[Match.STATUS_DRAW],
      score: 0,
    };
    if (statusStat[Match.STATUS_U1WIN] > statusStat[Match.STATUS_U2WIN]) {
      this.status = Match.STATUS_U1WIN;
      this.u1Stat.score = 3;
    } else if (statusStat[Match.STATUS_U1WIN] < statusStat[Match.STATUS_U2WIN]) {
      this.status = Match.STATUS_U2WIN;
      this.u2Stat.score = 3;
    } else {
      this.status = Match.STATUS_DRAW;
      this.u1Stat.score = 1;
      this.u2Stat.score = 1;
    }
  };

  /**
   * Mark a pending round as running
   *
   * @param  {MongoId} mdocid Match Id
   * @param  {MongoId} rid Round Id
   * @return {Match}
   */
  MatchSchema.statics.judgeStartRoundAsync = async function (mdocid, rid) {
    const mdoc = await Match.getMatchObjectByIdAsync(mdocid);
    const round = mdoc.rounds.find(rdoc => rdoc._id.equals(rid));
    if (round !== undefined) {
      round.status = Match.STATUS_RUNNING;
      round.beginJudgeAt = new Date();
      await mdoc.save();
    }
    return mdoc;
  };

  /**
   * Mark a round as completed (u1win, u2win or draw) or error.
   *
   * @param  {MongoId} mdocid Match Id
   * @param  {MongoId} rid Round Id
   * @param  {String} status
   * @param  {Object} extra Extra fields to be added to the round
   * @return {Match}
   */
  MatchSchema.statics.judgeCompleteRoundAsync = async function (mdocid, rid, status, extra = {}) {
    if (
      status !== Match.STATUS_U1WIN
      && status !== Match.STATUS_U2WIN
      && status !== Match.STATUS_DRAW
      && status !== Match.STATUS_SYSTEM_ERROR
    ) {
      throw new Error(`judgeCompleteRoundAsync: status ${status} is invalid`);
    }
    const mdoc = await Match.getMatchObjectByIdAsync(mdocid);
    const round = mdoc.rounds.find(rdoc => rdoc._id.equals(rid));
    if (round !== undefined) {
      round.status = status;
      round.endJudgeAt = new Date();
      if (!round.beginJudgeAt) {
        round.beginJudgeAt = new Date();
      }
      _.assign(round, extra);
      await mdoc.save();
    }
    return mdoc;
  };

  /**
   * Get all related matches for a submission
   * @param  {MongoId} sid
   * @return {[Match]}
   */
  MatchSchema.statics.getMatchesForSubmissionAsync = async function (sid) {
    const mdocs = await Match.find({
      $or: [
        { u1Submission: sid },
        { u2Submission: sid },
      ],
    }).sort({ _id: -1 }).limit(20);
    return mdocs;
  };

  /**
   * Update status of all matches
   */
  MatchSchema.statics.refreshAllMatchesAsync = async function () {
    let ret = {
      updated: 0,
      all: 0,
    };
    const cursor = Match.find().sort({ _id: 1 }).cursor();
    for (let mdoc = await cursor.next(); mdoc !== null; mdoc = await cursor.next()) {
      ret.all++;
      mdoc.updateMatchStatus();
      if (mdoc.isModified('status')) {
        ret.updated++;
        await mdoc.save();
      }
    }
    return ret;
  };

  /**
   * Given a list of allowed submissions _id, return all useable matches.
   * @return { [MatchModel] }
   * @param allowedSubmissionIds { [ObjectId] }
   */
  MatchSchema.statics.getPairwiseMatchesForSubmissionsAsync = async function(allowedSubmissionIds) {
    return await this
      .find({
        u1Submission: {
          $in: allowedSubmissionIds,
        },
        u2Submission: {
          $in: allowedSubmissionIds,
        },
      })
      .exec();
  };

  MatchSchema.index({ u1Submission: 1, u2Submission: -1 }, { unique: true });
  MatchSchema.index({ u2Submission: 1 });

  Match = mongoose.model('Match', MatchSchema);
  return Match;

};
<|endoftext|>


<commit_before>var fs = require("fs");
var md = require("markdown").markdown;
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();

libMdPath = '../contents/css-pre-processor/less.md';
contentsPath = '../contents/'

// Tools

function foreachDir(path, foreach) {
    // console.log("\nLOG: List " + path + " dir");
    fs.readdir(path, function(err, files) {
       if (err) {
           return console.error(err);
       }
       // console.log(files);
       for (var i in files) {
           foreach(files[i], i, files);
       }
    });

}

function readMd(filePath, callback, mdJSON) {

    // console.log("\nLOG: readMd:" + filePath)
    fs.readFile(filePath, 'utf-8', function(err, mdfile) {

        if (err) {
            console.error(err);
        } else {
            mdtree = md.parse(mdfile);
            result = callback(mdtree);
        }

    })

}

// Read libs

function readLibs(libMdPath) {

    // console.log('\nLOG: readLibs:' + libMdPath)

    mdJSON = {};

    function addMdTree(mdtree) {

        mdJSON = {
            'name': 'No name',
            'description': 'No description',
            'tabs': {},
        };

        var tempTabs;
        function addMdJSON(value, index, array) {
            if (index == 2) {
                mdJSON.description = value[1];
            } else if (value[1].level == 1) {
                mdJSON.name = value[2];
            } else if (value[1].level == 2) {
                tempTabs = value[2];
            } else {
                if ( tempTabs != undefined ) {
                    mdJSON.tabs[tempTabs] = md.toHTML(value[1]);
                }
            }
        }
        mdtree.forEach(addMdJSON);
        return mdJSON;

    }

    readMd(filePath, addMdTree, mdJSON);

}

// Read parts

function readParts(dirPath) {

    partsJSON = { 'libs': {} };
    foreachDir(dirPath, function(fileName, index, array){

        filePath = dirPath + '/' + fileName;

        if (fileName == '_meta.md') {
            readMd(filePath, function(mdtree){

                function addPartsJSON(value, index, array) {
                    if (index == 2) {
                        partsJSON.description = value[1];
                    } else if (value[1].level == 1) {
                        partsJSON.name = value[2];
                    }
                }
                mdtree.forEach(addPartsJSON);

            })

        } else {
            partsJSON.libs[fileName] = readLibs(filePath);
        }

        if (index == array.length - 1) { event.emit('eachend') }

    })

    event.on('eachend', function() {
        console.log(111111,partsJSON);
        return partsJSON;
    });

}

// Read contents

function readPartsOrder(fileName) {
    partsOrderJSON = JSON.parse(fs.readFileSync(fileName));
    return partsOrderJSON.partsOrder;
}

function readContents(fileName) {

    contentsJSON = {'parts': {}};

    filePath = contentsPath + fileName;
    console.log("\nLOG: readContents:" + filePath);
    if (fileName == 'parts.json') {
        contentsJSON.parstOrder = readPartsOrder(filePath);
    } else {
        contentsJSON.parts['fileName'] = readParts(filePath);
    }

    console.log(contentsJSON)
}

// Run read dir

contentstree = { 'partsOrder': [], 'parts': {} };
foreachDir(contentsPath, readContents);
// console.log("\nPrased contentstree:" + contentstree);
<commit_msg>Useless contents update.<commit_after>var fs = require("fs");
var md = require("markdown").markdown;
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();

libMdPath = '../contents/css-pre-processor/less.md';
contentsPath = '../contents/'

// Tools

function foreachDir(contentsTree, path, read) {
    fs.readdir(path, function(err, files) {
       if (err) {
           return console.error(err);
       }
       for (var i in files) {
           read(contentsTree, files[i], i, files);
       }
    });

}

function readMd(filePath, callback) {

    fs.readFile(filePath, 'utf-8', function(err, mdfile) {

        if (err) {
            console.error(err);
        } else {
            mdtree = md.parse(mdfile);
            result = callback(mdtree);
        }

    })

}

// Read libs

function readLibs(contentsTree, dirPath, treePath) {

    console.log(1111,treePath)

    function addMdTree(mdtree) {

        mdJSON = {
            'name': 'No name',
            'description': 'No description',
            'tabs': {},
        };

        var tempTabs;
        function addMdJSON(value, index, array) {
            if (index == 2) {
                eval('contentsTree.' + treePath + '.description = value[1]');
            } else if (value[1].level == 1) {
                eval('contentsTree.' + treePath + '.name = value[2]');
            } else if (value[1].level == 2) {
                tempTabs = value[2];
            } else {
                if ( tempTabs != undefined ) {
                    eval('contentsTree.' + treePath + '.tabs.' + tempTabs + ' =  md.toHTML(value[1])');
                }
            }
        }
        mdtree.forEach(addMdJSON);

        eval('contentsTree.' + treePath + ' = mdJSON');

    }

    readMd(filePath, addMdTree);

}

// Read parts

function readParts(contentsTree, dirPath, treePath) {

    eval('contentsTree.' + treePath + ' = {}');

    foreachDir(contentsTree, dirPath, function(contentsTree, fileName, index, array){

        filePath = dirPath + '/' + fileName;

        if (fileName == '_meta.md') {
            readMd(filePath, function(mdtree){

                function addPartsJSON(value, index, array) {
                    if (index == 2) {
                        eval('contentsTree.' + treePath + '.description = value[1]');
                    } else if (value[1].level == 1) {
                        eval('contentsTree.' + treePath + '.name = value[2]');
                    }
                }
                mdtree.forEach(addPartsJSON);

                console.log(contentsTree);

            })

        } else {
            libTreePath = treePath + '.libs.' + fileName.substring(0,fileName.length - 3);
            eval('contentsTree.' + libTreePath + ' = {}');
            readLibs(contentsTree, filePath, libTreePath);
        }

    })

}

// Read contents

function readPartsOrder(contentsTree, filePath, treePath) {
    eval('contentsTree.' + treePath + ' = ' + fs.readFileSync(filePath));
}

function readContents(contentsTree, fileName, treePath) {

    filePath = contentsPath + fileName;
    if (fileName == 'parts.json') {
        readPartsOrder(contentsTree, filePath, 'partsOrder');
    } else {
        readParts(contentsTree, filePath, 'parts.'+fileName);
    }

}

// Run read dir

contentsTree = { 'partsOrder': [], 'parts': {} };
foreachDir(contentsTree, contentsPath, readContents);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013 by Animatron.
 * All rights are reserved.
 *
 * Animatron Player is licensed under the MIT License, see LICENSE.
 *
 * @VERSION
 */

(function() { // anonymous wrapper to exclude global context clash
  var C = anm.C;
  var _ResMan = __anm.resource_manager;

  C.MOD_AUDIO = 'audio';
  if (anm.M[C.MOD_AUDIO]) throw new Error('AUDIO module already enabled');

  anm.M[C.MOD_AUDIO] = {};

  var E = anm.Element;

  // Element functions
  // ----------------------------------------------------------------------------------------------------------------

  var _audio_customRender = function(gtime, ltime, ctx) {
    // TODO: remove
    return false;
  };

  var _onAudioStart = function(ltime, duration) {
    if (!this._audio_is_loaded || this._audio_is_playing) {
      return false;
    }

    this._audio_is_playing = true;
    this._audio.currentTime = this._audio_band_offset + ltime;
    this._audio.play();
  };

  var _onAudioStop = function(ltime, duration) {
    if (this._audio_is_playing) {
      this._audio.pause();
      this._audio_is_playing = false;
    }
  };

  E.prototype.importCustomData = function(object, type, importer) {
    if ((14 == type)/*ANM*/ ||
        ("0e" == type)/*ANM_INTACT*/) {
      if (importer == "ANM") {
        /** audio **/
        /*
         * array {
         *     14;                         // 0
         *     string;                     // 1, url
         *     number;                     // 2, band offset
         * } *audio_element*;
         */
        this._audio_url = this._audio_format_url(object[1]);
        this._audio_band_offset = object[2];
      } else if (importer == "ANM_INTACT") {
        this._audio_band_offset = object.bandOffset;
        this._audio_url = this._audio_format_url(object.url);
      }

      this.isAudio = true;
      this._audio = null;
      this._audio_is_loaded = false;
      this._audio_is_playing = false;

      this.on(C.X_START, _onAudioStart);
      this.on(C.X_STOP, _onAudioStop);
      this.on(C.S_STOP, _onAudioStop);
      this.on(C.S_PAUSE, _onAudioStop);

      // assign custom render function
      this.__frameProcessors.push(_audio_customRender);

      this._audio_load();
    }
  };

  E.prototype._audio_format_url = function(url) {
    return url + (this._mpeg_supported() ? ".mp3" : ".ogg");
  };

  E.__test_elm = null;
  E.prototype._mpeg_supported = function() {
    var a = E.__test_elm ? E.__test_elm : (E.__test_elm = document.createElement('audio'), E.__test_elm);
    return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
  }

  var prev__hasRemoteResources = E.prototype._hasRemoteResources;
  E.prototype._hasRemoteResources = function() {
    return prev__hasRemoteResources.call(this, arguments) || this.isAudio;
  }

  var prev__getRemoteResources = E.prototype._getRemoteResources;
  E.prototype._getRemoteResources = function() {
    var prev = prev__getRemoteResources.call(this, arguments);
    if (!this.isAudio && !prev) return null;
    if (!this.isAudio) return prev;
        // return [ this._audio_url ].concat(prev || [])
    return prev ? [ this._audio_url ].concat(prev) : [ this._audio_url ];
  }

  E.prototype._audio_load = function() {
    var me = this;

    _ResMan.loadOrGet(me._audio_url,
      function(onsuccess, onerror) { // loader
          var el = document.createElement("audio");
          el.setAttribute("preload", "auto");
          el.addEventListener("loadeddata", function(e) {
            onsuccess(el);
          }, false);
          el.addEventListener("error", onerror, false);

          try {
            document.getElementsByTagName("body")[0].appendChild(el);
            el.src = me._audio_url;
          } catch(e) {
            onerror(e);
            throw new Error('Audio at ' + me._audio_url + ' is not accessible');
          }
      },
      function(audio) {  // oncomplete
          me._audio = audio;
          me._audio_is_loaded = true;
          if (callback) callback.call(me, image);
      });

  };

})();<commit_msg>fix callback mistypes<commit_after>/*
 * Copyright (c) 2011-2013 by Animatron.
 * All rights are reserved.
 *
 * Animatron Player is licensed under the MIT License, see LICENSE.
 *
 * @VERSION
 */

(function() { // anonymous wrapper to exclude global context clash
  var C = anm.C;
  var _ResMan = __anm.resource_manager;

  C.MOD_AUDIO = 'audio';
  if (anm.M[C.MOD_AUDIO]) throw new Error('AUDIO module already enabled');

  anm.M[C.MOD_AUDIO] = {};

  var E = anm.Element;

  // Element functions
  // ----------------------------------------------------------------------------------------------------------------

  var _audio_customRender = function(gtime, ltime, ctx) {
    // TODO: remove
    return false;
  };

  var _onAudioStart = function(ltime, duration) {
    if (!this._audio_is_loaded || this._audio_is_playing) {
      return false;
    }

    this._audio_is_playing = true;
    this._audio.currentTime = this._audio_band_offset + ltime;
    this._audio.play();
  };

  var _onAudioStop = function(ltime, duration) {
    if (this._audio_is_playing) {
      this._audio.pause();
      this._audio_is_playing = false;
    }
  };

  E.prototype.importCustomData = function(object, type, importer) {
    if ((14 == type)/*ANM*/ ||
        ("0e" == type)/*ANM_INTACT*/) {
      if (importer == "ANM") {
        /** audio **/
        /*
         * array {
         *     14;                         // 0
         *     string;                     // 1, url
         *     number;                     // 2, band offset
         * } *audio_element*;
         */
        this._audio_url = this._audio_format_url(object[1]);
        this._audio_band_offset = object[2];
      } else if (importer == "ANM_INTACT") {
        this._audio_band_offset = object.bandOffset;
        this._audio_url = this._audio_format_url(object.url);
      }

      this.isAudio = true;
      this._audio = null;
      this._audio_is_loaded = false;
      this._audio_is_playing = false;

      this.on(C.X_START, _onAudioStart);
      this.on(C.X_STOP, _onAudioStop);
      this.on(C.S_STOP, _onAudioStop);
      this.on(C.S_PAUSE, _onAudioStop);

      // assign custom render function
      this.__frameProcessors.push(_audio_customRender);

      this._audio_load();
    }
  };

  E.prototype._audio_format_url = function(url) {
    return url + (this._mpeg_supported() ? ".mp3" : ".ogg");
  };

  E.__test_elm = null;
  E.prototype._mpeg_supported = function() {
    var a = E.__test_elm ? E.__test_elm : (E.__test_elm = document.createElement('audio'), E.__test_elm);
    return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
  }

  var prev__hasRemoteResources = E.prototype._hasRemoteResources;
  E.prototype._hasRemoteResources = function() {
    return prev__hasRemoteResources.call(this, arguments) || this.isAudio;
  }

  var prev__getRemoteResources = E.prototype._getRemoteResources;
  E.prototype._getRemoteResources = function() {
    var prev = prev__getRemoteResources.call(this, arguments);
    if (!this.isAudio && !prev) return null;
    if (!this.isAudio) return prev;
        // return [ this._audio_url ].concat(prev || [])
    return prev ? [ this._audio_url ].concat(prev) : [ this._audio_url ];
  }

  E.prototype._audio_load = function() {
    var me = this;

    _ResMan.loadOrGet(me._audio_url,
      function(onsuccess, onerror) { // loader
          var el = document.createElement("audio");
          el.setAttribute("preload", "auto");
          el.addEventListener("loadeddata", function(e) {
            onsuccess(el);
          }, false);
          el.addEventListener("error", onerror, false);

          try {
            document.getElementsByTagName("body")[0].appendChild(el);
            el.src = me._audio_url;
          } catch(e) {
            onerror(e);
            throw new Error('Audio at ' + me._audio_url + ' is not accessible');
          }
      },
      function(audio) {  // oncomplete
          me._audio = audio;
          me._audio_is_loaded = true;
          if (callback) callback.call(me, audio);
      });

  };

})();<|endoftext|>


<commit_before>var cls = require("./lib/class"),
	Player = require("./entities/player"),
	Bullet = require("./entities/bullet"),
	log = require('./log'),
	WorldMap = require("./worldmap"),
	_ = require("underscore"),
	Box2D = require('./lib/box2d'),
	EntityFactory = require('./entityFactory');

var b2Vec2 = Box2D.Common.Math.b2Vec2;
if (process.env.NodeTimeKey) {
	require('nodetime').profile({
		accountKey: process.env.NodeTimeKey,
		appName: 'Aux'
	});
}
var logger = require("./gamelogger");

module.exports = cls.Class.extend({
	init: function (ups, map_filepath, engine, server, debug) {
		this.ups = ups;
		this.engine = engine;
		this.server = server;
		this.debug = debug;
		this.map = new WorldMap(map_filepath);
		this.engine.addEntities(this.map.entities);
		this.onPlayerConnect(this.playerConnect);
		this.onPlayerDisconnect(this.playerDisconnect);
		this.playersCount = 0;
	},
	playerConnect: function (connection) {
		if (this.playersCount === 0) {
			logger.updateDate();
		}
		this.playersCount += 1;
		require('nodetime').metric("World", "Players", this.playersCount, "", "set");
		var self = this;
		var player = new Player(connection, connection.id, this.debug);
		var spawnPosition = this.map.getSpawnPoint();
		player.setPosition(spawnPosition.x, spawnPosition.y);
		log.info("Player " + player.id + " connected");
		logger.write(player.connection, new Buffer(JSON.stringify(player.getPosition())), logger.MsgType.Connect);
		connection.onClose(function () {
			self.disconnect_callback(player);
		});

		log.info("Send Welcome to player " + player.id);
		player.send(Constants.Types.Messages.Welcome, player.getBaseState());

		player.onShoot(function () {
			if (player.bullets > 0) {
				var bullet = EntityFactory.createBullet(player);
				self.engine.addEntity(bullet);
				bullet.onRemove(self.engine.removeEntity.bind(self.engine));
				var angle = player.getAngle() * Math.PI / 180,
					x = Math.cos(angle) * Bullet.SpeedRatio,
					y = -Math.sin(angle) * Bullet.SpeedRatio;
				bullet.body.SetAngle(angle);
				bullet.body.ApplyImpulse(new b2Vec2(x, y), new b2Vec2(0, 0));
				player.bullets -= 1;
			}
		});

		player.onRespawn(function () {
			player.health = Player.StandartHealth;
			player.bullets = Player.StandartBullets;
			player.animationType = "idle";
			var spawnPosition = self.map.getSpawnPoint();
			player.setPosition(spawnPosition.x, spawnPosition.y);
		});

		this.engine.addEntity(player);
	},
	playerDisconnect: function (player) {
		this.playersCount -= 1;
		require('nodetime').metric("World", "Players", this.playersCount, "", "set");
		logger.write(player.connection, new Buffer(player.id), logger.MsgType.Disconnect);
		this.engine.removeEntity(player.id);
	},
	updateWorld: function () {
		var self = this;
		_.each(self.engine.getEntities(), function (entity) {
			entity.update();
		});
	},
	updatePlayers: function () {
		var self = this;
		var players = self.engine.getEntities(function (entity) {
			return entity instanceof Player;
		});
		_.each(players, function (player) {
			var entities = self.engine.getEntities(function (entity) {
				return self.engine.isVisible(player, entity) &&
				(entity instanceof Player || entity instanceof Bullet);
			});
			player.sendEntities(entities);
		});
	},
	run: function () {
		var ticks = 0;
		var start = new Date() / 1000;
		var self = this;
		this.worldUpdater = setInterval(function () {
			ticks += 1;
			self.engine.tick(1000.0 / self.ups);
			self.updateWorld();
			self.updatePlayers();
		}, 1000 / this.ups);
		setInterval(function () {
			var now = new Date() / 1000;
			require('nodetime').metric("World", "Players", self.playersCount, "", "set");
			require('nodetime').metric("World", "Ticks per second", ticks / (now - start), "ticks", "set");
		}, 5000);
		setTimeout(function () {
			self.ready_callback();
		}, 1000 / this.ups);
	},
	stop: function () {
		clearInterval(this.worldUpdater);
	},
	onPlayerConnect: function (callback) {
		this.connect_callback = callback;
	},
	onPlayerDisconnect: function (callback) {
		this.disconnect_callback = callback;
	},
	onReady: function (callback) {
		this.ready_callback = callback;
	},
	ready_callback: function () {
		log.info("World started");
	}
});<commit_msg>do not overwrite players count every 5 seconds<commit_after>var cls = require("./lib/class"),
	Player = require("./entities/player"),
	Bullet = require("./entities/bullet"),
	log = require('./log'),
	WorldMap = require("./worldmap"),
	_ = require("underscore"),
	Box2D = require('./lib/box2d'),
	EntityFactory = require('./entityFactory');

var b2Vec2 = Box2D.Common.Math.b2Vec2;
if (process.env.NodeTimeKey) {
	require('nodetime').profile({
		accountKey: process.env.NodeTimeKey,
		appName: 'Aux'
	});
}
var logger = require("./gamelogger");

module.exports = cls.Class.extend({
	init: function (ups, map_filepath, engine, server, debug) {
		this.ups = ups;
		this.engine = engine;
		this.server = server;
		this.debug = debug;
		this.map = new WorldMap(map_filepath);
		this.engine.addEntities(this.map.entities);
		this.onPlayerConnect(this.playerConnect);
		this.onPlayerDisconnect(this.playerDisconnect);
		this.playersCount = 0;
	},
	playerConnect: function (connection) {
		if (this.playersCount === 0) {
			logger.updateDate();
		}
		this.playersCount += 1;
		require('nodetime').metric("World", "Players", this.playersCount, "", "set");
		var self = this;
		var player = new Player(connection, connection.id, this.debug);
		var spawnPosition = this.map.getSpawnPoint();
		player.setPosition(spawnPosition.x, spawnPosition.y);
		log.info("Player " + player.id + " connected");
		logger.write(player.connection, new Buffer(JSON.stringify(player.getPosition())), logger.MsgType.Connect);
		connection.onClose(function () {
			self.disconnect_callback(player);
		});

		log.info("Send Welcome to player " + player.id);
		player.send(Constants.Types.Messages.Welcome, player.getBaseState());

		player.onShoot(function () {
			if (player.bullets > 0) {
				var bullet = EntityFactory.createBullet(player);
				self.engine.addEntity(bullet);
				bullet.onRemove(self.engine.removeEntity.bind(self.engine));
				var angle = player.getAngle() * Math.PI / 180,
					x = Math.cos(angle) * Bullet.SpeedRatio,
					y = -Math.sin(angle) * Bullet.SpeedRatio;
				bullet.body.SetAngle(angle);
				bullet.body.ApplyImpulse(new b2Vec2(x, y), new b2Vec2(0, 0));
				player.bullets -= 1;
			}
		});

		player.onRespawn(function () {
			player.health = Player.StandartHealth;
			player.bullets = Player.StandartBullets;
			player.animationType = "idle";
			var spawnPosition = self.map.getSpawnPoint();
			player.setPosition(spawnPosition.x, spawnPosition.y);
		});

		this.engine.addEntity(player);
	},
	playerDisconnect: function (player) {
		this.playersCount -= 1;
		require('nodetime').metric("World", "Players", this.playersCount, "", "set");
		logger.write(player.connection, new Buffer(player.id), logger.MsgType.Disconnect);
		this.engine.removeEntity(player.id);
	},
	updateWorld: function () {
		var self = this;
		_.each(self.engine.getEntities(), function (entity) {
			entity.update();
		});
	},
	updatePlayers: function () {
		var self = this;
		var players = self.engine.getEntities(function (entity) {
			return entity instanceof Player;
		});
		_.each(players, function (player) {
			var entities = self.engine.getEntities(function (entity) {
				return self.engine.isVisible(player, entity) &&
				(entity instanceof Player || entity instanceof Bullet);
			});
			player.sendEntities(entities);
		});
	},
	run: function () {
		var ticks = 0;
		var start = new Date() / 1000;
		var self = this;
		this.worldUpdater = setInterval(function () {
			ticks += 1;
			self.engine.tick(1000.0 / self.ups);
			self.updateWorld();
			self.updatePlayers();
		}, 1000 / this.ups);
		setInterval(function () {
			var now = new Date() / 1000;
			//require('nodetime').metric("World", "Players", self.playersCount, "", "set");
			require('nodetime').metric("World", "Ticks per second", ticks / (now - start), "ticks", "set");
		}, 5000);
		setTimeout(function () {
			self.ready_callback();
		}, 1000 / this.ups);
	},
	stop: function () {
		clearInterval(this.worldUpdater);
	},
	onPlayerConnect: function (callback) {
		this.connect_callback = callback;
	},
	onPlayerDisconnect: function (callback) {
		this.disconnect_callback = callback;
	},
	onReady: function (callback) {
		this.ready_callback = callback;
	},
	ready_callback: function () {
		log.info("World started");
	}
});<|endoftext|>


<commit_before>
;(function( FooFighter ){

function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

function randInRange(min, max) {
    return Math.random() * (max - min) + min;
}

FooFighter.Util = {
    guid: guid,
    randInRange: randInRange
};

})(FooFighter);
<commit_msg>update docstring<commit_after>/**
 * Util.js
 *
 * FooFighter.js — Util module
 *
 * Convenience utility functions for Fun and Profit™
 */

;(function( FooFighter ){

function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

function randInRange(min, max) {
    return Math.random() * (max - min) + min;
}

FooFighter.Util = {
    guid: guid,
    randInRange: randInRange
};

})(FooFighter);
<|endoftext|>


<commit_before><commit_msg>better variables and methods naming<commit_after><|endoftext|>


<commit_before>// 所有功能必须包含在 WeixinApi.ready 中进行
var weixin = (function(zHelper) {

    zHelper.log("Wait for WeixinApi.ready!");
    var wxData = {
        "imgUrl": 'http://rent.zzn.im/Logo_108.jpg'
        // "link": 'http://www.baidufe.com',
        // "desc": '大家好，我是Alien，Web前端&Android客户端码农，喜欢技术上的瞎倒腾！欢迎多交流',
        // "title": "大家好，我是赵先烈"
    };

    // 分享的回调
    var wxCallbacks = {
        // 分享操作开始之前
        ready: function () {
            // 你可以在这里对分享的数据进行重组
            zHelper.log("weixin ready");
        },
        // 分享被用户自动取消
        cancel: function (resp) {
            zHelper.log("weixin cancel");
            // 你可以在你的页面上给用户一个小Tip，为什么要取消呢？
        },
        // 分享失败了
        fail: function (resp) {
            zHelper.log("weixin fail");
            // 分享失败了，是不是可以告诉用户：不要紧，可能是网络问题，一会儿再试试？
        },
        // 分享成功
        confirm: function (resp) {
            zHelper.log("weixin confirm");
            // 分享成功了，我们是不是可以做一些分享统计呢？
        },
        // 整个分享过程结束
        all: function (resp) {
            zHelper.log("weixin all");
            // 如果你做的是一个鼓励用户进行分享的产品，在这里是不是可以给用户一些反馈了？
        }
    };

    WeixinApi.ready(function (Api) {
        zHelper.log("WeixinApi.ready!");
        // 微信分享的数据
        // 用户点开右上角popup菜单后，点击分享给好友，会执行下面这个代码
        Api.shareToFriend(wxData, wxCallbacks);

        // 点击分享到朋友圈，会执行下面这个代码
        Api.shareToTimeline(wxData, wxCallbacks);

        // 点击分享到腾讯微博，会执行下面这个代码
        Api.shareToWeibo(wxData, wxCallbacks);
    });
    return {
        wxData: wxData,
        wxCallbacks: wxCallbacks
    };
})(zHelper);<commit_msg>Add debug mode<commit_after>// 所有功能必须包含在 WeixinApi.ready 中进行
var weixin = (function(zHelper) {
    if (zHelper.isDebug()) {
        WeixinApi.enableDebugMode();
    }
    zHelper.log("Wait for WeixinApi.ready!");
    var wxData = {
        "imgUrl": 'http://rent.zzn.im/Logo_108.jpg'
        // "link": 'http://www.baidufe.com',
        // "desc": '大家好，我是Alien，Web前端&Android客户端码农，喜欢技术上的瞎倒腾！欢迎多交流',
        // "title": "大家好，我是赵先烈"
    };

    // 分享的回调
    var wxCallbacks = {
        // 分享操作开始之前
        ready: function () {
            // 你可以在这里对分享的数据进行重组
            zHelper.log("weixin ready");
        },
        // 分享被用户自动取消
        cancel: function (resp) {
            zHelper.log("weixin cancel");
            // 你可以在你的页面上给用户一个小Tip，为什么要取消呢？
        },
        // 分享失败了
        fail: function (resp) {
            zHelper.log("weixin fail");
            // 分享失败了，是不是可以告诉用户：不要紧，可能是网络问题，一会儿再试试？
        },
        // 分享成功
        confirm: function (resp) {
            zHelper.log("weixin confirm");
            // 分享成功了，我们是不是可以做一些分享统计呢？
        },
        // 整个分享过程结束
        all: function (resp) {
            zHelper.log("weixin all");
            // 如果你做的是一个鼓励用户进行分享的产品，在这里是不是可以给用户一些反馈了？
        }
    };

    WeixinApi.ready(function (Api) {
        zHelper.log("WeixinApi.ready!");
        // 微信分享的数据
        // 用户点开右上角popup菜单后，点击分享给好友，会执行下面这个代码
        Api.shareToFriend(wxData, wxCallbacks);

        // 点击分享到朋友圈，会执行下面这个代码
        Api.shareToTimeline(wxData, wxCallbacks);

        // 点击分享到腾讯微博，会执行下面这个代码
        Api.shareToWeibo(wxData, wxCallbacks);
    });
    return {
        wxData: wxData,
        wxCallbacks: wxCallbacks
    };
})(zHelper);<|endoftext|>