<commit_before>const RuleEngine = require('node-rules');
const _ = require('lodash');
const config = require('config');
const redis = require('redis');
const getBalances = require('./lib/get-balances');
const Account = require('./lib/account');
const getTicker = require('./lib/get-ticker');
const getRange = require('./lib/get-range');
const getUpperSellPercentage = require('./lib/get-upper-sell-percentage');
const buyLimit = require('./lib/buy-limit');
const sellLimit = require('./lib/sell-limit');
const tradeStub = require('./lib/trade-stub');
const helper = require('./helper');
const poll = require('./poll');
const bunyan = require('bunyan');

const log = bunyan.createLogger({ name: 'siena' });

const redisClient = redis.createClient();
const redisClientForCacheOperations = redis.createClient();
const redisClientMessageQueue = redis.createClient();
redisClient.on('error', redisError => log.error(redisError));

let crossover;
let lastTrade;
let lastTradeTime = 0;
let lastBuyPrice = 0;
let lastSellPrice = 0;
let upperSellPercentage = config.get('strategy.upperSellPercentage');
let transactionLock = false;
const sienaAccount = new Account();

// Automation rules
const rules = [{
  condition: function condition(R) {
    R.when(_.has(this, 'netAssetValue') && (this.netAssetValue < config.get('sienaAccount.criticalPoint')));
  },
  consequence: function consequence(R) {
    this.actions = ['sellSecurity', 'halt'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'bittrexAccountBalances'));
  },
  consequence: function consequence(R) {
    this.actions = ['compartmentaliseAccount'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'movingAverageShort') &&
      _.has(this, 'movingAverageMid') &&
      _.has(this, 'movingAverageLong'));
  },
  consequence: function consequence(R) {
    this.actions = ['getMarketTrend'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'event') &&
      _.has(this, 'market') &&
      _.has(this, 'lastTrade') &&
      this.event === 'crossover' &&
      this.market === 'VOLATILE-LOW' &&
      this.lastTrade !== 'BUY');
  },
  consequence: function consequence(R) {
    // Buy security on the cheap as long as it isn't a bear market.
    this.actions = ['buySecurity'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'event') &&
      _.has(this, 'market') &&
      _.has(this, 'lastTrade') &&
      _.has(this, 'currentBidPrice') &&
      _.has(this, 'lastSellPrice') &&
      this.event === 'crossover' &&
      this.market !== 'BEAR' &&
      this.lastTrade === 'SELL-LOW' &&
      this.currentBidPrice < this.lastSellPrice);
  },
  consequence: function consequence(R) {
    // We've incurred a loss the last sale so buy it on the cheaper than your last sell price.
    this.actions = ['buySecurity'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'event') &&
      _.has(this, 'lastTrade') &&
      _.has(this, 'market') &&
      _.has(this, 'lastBuyPrice') &&
      _.has(this, 'currentBidPrice') &&
      _.has(this, 'rangePercentage') &&
      this.event === 'crossover' &&
      this.currentBidPrice > (this.lastBuyPrice + (upperSellPercentage * this.lastBuyPrice)) &&
      this.lastTrade === 'BUY' &&
      this.market !== 'BULL');
  },
  consequence: function consequence(R) {
    // You've got a profit so cash in!
    this.actions = ['sellSecurity'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'lastTrade') &&
      _.has(this, 'market') &&
      _.has(this, 'lastBuyPrice') &&
      _.has(this, 'currentBidPrice') &&
      this.currentBidPrice < (this.lastBuyPrice - (config.get('strategy.lowerSellPercentage') * this.lastBuyPrice)) &&
      this.lastTrade === 'BUY' &&
      this.market === 'BEAR');
  },
  consequence: function consequence(R) {
    // This is a bear market, sell and wait for better buying opportunity.
    this.actions = ['sellSecurity'];
    R.stop();
  },
}];

const getMarketTrend = async (movingAverageShort, movingAverageMid, movingAverageLong) => {
  const currentMarket = {};
  if (movingAverageShort > movingAverageMid) {
    currentMarket.trend = 'UP';
  } else {
    currentMarket.trend = 'DOWN';
  }

  if (movingAverageShort >= movingAverageMid && movingAverageMid >= movingAverageLong) {
    currentMarket.market = 'BULL';
  } else if (movingAverageLong >= movingAverageMid && movingAverageMid >= movingAverageShort) {
    currentMarket.market = 'BEAR';
  } else if (movingAverageMid >= movingAverageShort && movingAverageShort >= movingAverageLong) {
    currentMarket.market = 'VOLATILE-MID';
  } else if (movingAverageLong >= movingAverageShort && movingAverageShort >= movingAverageMid) {
    currentMarket.market = 'VOLATILE-RECOVERY';
  } else if (movingAverageMid >= movingAverageLong && movingAverageLong >= movingAverageShort) {
    currentMarket.market = 'VOLATILE-LOW';
  } else if (movingAverageShort >= movingAverageLong && movingAverageLong >= movingAverageMid) {
    currentMarket.market = 'VOLATILE';
  } else {
    currentMarket.market = 'FLAT';
  }

  log.info(`getMarketTrend, trend : ${currentMarket.trend}, market: ${(currentMarket.market || 'nevermind')}`);

  if (crossover === undefined) {
    crossover = currentMarket;
  }

  let bearTicker;
  if (currentMarket.market === 'BEAR' && lastTrade === 'BUY' && lastBuyPrice > 0) {
    const bearFact = _.cloneDeep(currentMarket);
    bearFact.lastTrade = lastTrade;
    bearFact.lastBuyPrice = lastBuyPrice;

    bearTicker = await getTicker(config.get('bittrexMarket'));
    bearFact.currentBidPrice = bearTicker.Bid;

    // We need to publish this fact to the rules engine so that we can SELL
    // at the right time instead of waiting for a crossover moment.
    redisClientMessageQueue.publish('facts', JSON.stringify(bearFact));
  }

  if (_.isEqual(crossover, currentMarket)) {
    // The market has not crossedOver based on the last value
    return false;
  }

  // Market has crossedOver
  crossover = currentMarket;
  const fact = _.cloneDeep(crossover);
  fact.event = 'crossover';
  fact.crossoverTime = new Date().getTime();
  fact.lastTradeTime = lastTradeTime;
  fact.lastTrade = lastTrade;

  const tasks = [
    getRange(config.get('bittrexMarket')),
    bearTicker || getTicker(config.get('bittrexMarket')),
  ];

  const [range, ticker] = await Promise.all(tasks);
  fact.currentBidPrice = ticker.Bid;
  fact.rangePercentage = range / ticker.Bid;

  if (lastBuyPrice > 0) {
    fact.lastBuyPrice = lastBuyPrice;
  }

  if (lastSellPrice > 0) {
    fact.lastSellPrice = lastSellPrice;
  }

  log.info(`getMarketTrend, crossoverTime: ${fact.crossoverTime}`);

  redisClientMessageQueue.publish('facts', JSON.stringify(fact));

  // Cache crossover for strategy analysis
  // Cache recommendation
  const crossoverCacheData = {
    movingAverageShort,
    movingAverageMid,
    movingAverageLong,
    trend: currentMarket.trend,
    market: currentMarket.market,
    bidPrice: ticker.Bid,
    askPrice: ticker.Ask,
    timestamp: new Date(),
  };

  redisClientForCacheOperations.zadd([`${config.get('bittrexMarket')}-crossovers`, new Date().getTime(), `${JSON.stringify(crossoverCacheData)}`]);
  return currentMarket;
};

const compartmentaliseAccount = (bittrexAccountBalances) => {
  sienaAccount.setBittrexBalance(bittrexAccountBalances);
  redisClientMessageQueue.publish('facts', JSON.stringify({ accountBalance: sienaAccount.getBalanceNumber() }));
};

const updateBalance = async () => {
  const balances = await getBalances();
  redisClientMessageQueue.publish('facts', JSON.stringify({ bittrexAccountBalances: balances }));
  return balances;
};

const updateLastTradeTime = async (expectedBalance, action, price = undefined) => {
  const account = new Account();
  const balance = account.setBittrexBalance(await updateBalance());
  log.info(`updateLastTradeTime: actual balance:${balance}, expected balance: ${expectedBalance}.`);
  if (balance.toFixed(2) === expectedBalance.toFixed(2)) {
    if (action === 'BUY') {
      lastBuyPrice = price;

      // Calculate the SELL trigger prices
      upperSellPercentage = await getUpperSellPercentage(price);
      const upperBand = upperSellPercentage * parseFloat(lastBuyPrice);
      const lowerBand = config.get('strategy.lowerSellPercentage') * parseFloat(lastBuyPrice);
      const lowerSellTriggerPrice = parseFloat(lastBuyPrice) - lowerBand;
      const upperSellTriggerPrice = parseFloat(lastBuyPrice) + upperBand;
      log.info(`getMarketTrend, Upper SELL trigger price:${upperSellTriggerPrice}`);
      log.info(`getMarketTrend, Lower SELL trigger price:${lowerSellTriggerPrice}`);
    } else {
      lastSellPrice = price;
    }

    // trade was successful
    lastTradeTime = new Date().getTime();
    lastTrade = action;
    transactionLock = false;
    log.info(`updateLastTradeTime: lastTradeTime: ${lastTradeTime}, lastBuyPrice: ${(lastBuyPrice || 'nevermind')}`);
  } else {
    log.error('updateLastTradeTime, Error: lastTrade unsuccessful');

    // Why didn't bittrex fill the last order even after `balancePollInterval` seconds ?
    // Terminate Siena for now, investiage if your order was filled on bittrex and restart Siena

    // TODO : Maybe notify yourself that Siena has terminated due to an unfilled order
    process.exit();
  }
};

const buySecurity = async () => {
  if (config.get('trade') === false) {
    log.info('buySecurity, trade: false. Skipping security trades');
    return (false);
  }

  if (transactionLock) {
    log.info('buySecurity, transactionLock: true. Transaction in progress.');
    return (false);
  }

  transactionLock = true;
  const tasks = [
    getBalances(),
    getTicker(config.get('bittrexMarket')),
  ];
  const timeSinceLastTrade = new Date().getTime() - lastTradeTime;
  if (timeSinceLastTrade < config.get('balancePollInterval')) {
    log.warn(`buySecurity, timeSinceLastTrade: ${helper.millisecondsToHours(timeSinceLastTrade)}. Should maybe passing this buy signal?`);
  }

  const [bittrexBalances, ticker] = await Promise.all(tasks);
  sienaAccount.setBittrexBalance(bittrexBalances);
  if (sienaAccount.getBalanceNumber() < 1) {
    log.error(`buySecurity Error, account Balance : ${sienaAccount.getBalanceNumber()}. Not enough balance`);
    return (false);
  }

  const buyQuantity = sienaAccount.getTradeAmount() / ticker.Ask;
  const commission = tradeStub.getCommission(buyQuantity, ticker.Ask);
  const buyLesserQuantity = (sienaAccount.getTradeAmount() - commission) / ticker.Ask;

  log.info(`buySecurity: Buy ${buyLesserQuantity}${config.get('sienaAccount.securityCurrency')} for ${ticker.Ask} on ${new Date()}`);
  const order = await buyLimit(config.get('bittrexMarket'), buyLesserQuantity, ticker.Ask);
  log.info(`buySecurity, buyOrderUuid: ${order.uuid}`);
  const trade = tradeStub.buy(buyLesserQuantity, ticker.Ask);
  const expectedBalance = sienaAccount.getBalanceNumber() - trade.total;

  // Assume that this order gets filled and then update the balance
  setTimeout(() => { updateLastTradeTime(expectedBalance, 'BUY', ticker.Ask); }, config.get('balancePollInterval'));
  return (true);
};

const sellSecurity = async () => {
  if (config.get('trade') === false) {
    log.info('sellSecurity, trade: false. Skipping security trades');
    return (false);
  }

  if (transactionLock) {
    log.info('sellSecurity, transactionLock: true. Transaction in progress.');
  }

  transactionLock = true;

  const tasks = [
    getBalances(),
    getTicker(config.get('bittrexMarket')),
  ];

  let ticker;
  let bittrexBalances;
  try {
    [bittrexBalances, ticker] = await Promise.all(tasks);
  } catch (err) {
    log.error(`sellSecurity, Error : ${err}`);
    return (false);
  }

  sienaAccount.setBittrexBalance(bittrexBalances);

  const securityQuantity = sienaAccount.getBittrexBalance();
  if (securityQuantity > 0) {
    log.info(`sellSecurity: Sell ${securityQuantity}${config.get('sienaAccount.securityCurrency')} for ${ticker.Bid}`);
    const order = await sellLimit(config.get('bittrexMarket'), securityQuantity, ticker.Bid);
    log.info(`sellSecurity, sellOrderUuid: ${order.uuid}`);
    const trade = tradeStub.sell(securityQuantity, ticker.Bid);
    const expectedBalance = sienaAccount.getBalanceNumber() + trade.total;

    // Assume that this order gets filled and then update the balance
    setTimeout(() => {
      updateLastTradeTime(expectedBalance,
        (parseFloat(ticker.Bid) > parseFloat(lastBuyPrice) ? 'SELL-HIGH' : 'SELL-LOW'),
        ticker.Bid);
    }, config.get('balancePollInterval'));
    return (true);
  }

  log.error('sellSecurity Error: No security to Sell');
  return (false);
};

// initialize the rule engine
const R = new RuleEngine(rules);

redisClient.on('message', (channel, message) => {
  try {
    const fact = JSON.parse(message);

    // Pass the fact on to the rule engine for results
    R.execute(fact, (result) => {
      log.info(`Siena Rules : fact : ${JSON.stringify(fact)}`);

      if (_.includes(result.actions, 'infer') && _.has(result, 'fact')) {
        redisClientMessageQueue.publish('facts', JSON.stringify(result.fact));
      }

      if (_.includes(result.actions, 'getMarketTrend')
          && _.has(result, 'movingAverageShort')
          && _.has(result, 'movingAverageMid')
          && _.has(result, 'movingAverageLong')) {
        getMarketTrend(result.movingAverageShort,
          result.movingAverageMid, result.movingAverageLong);
      }

      if (_.includes(result.actions, 'compartmentaliseAccount') && _.has(result, 'bittrexAccountBalances')) {
        compartmentaliseAccount(result.bittrexAccountBalances);
      }

      if (_.includes(result.actions, 'buySecurity')) {
        buySecurity();
      }

      if (_.includes(result.actions, 'sellSecurity')) {
        sellSecurity();
      }
    });
  } catch (error) {
    log.error('Siena Rules : Error : ', error);
  }
});

// Keep polling the moving averages
setInterval(() => poll(config.get('bittrexMarket')), 5000);

// Listen for facts
redisClient.subscribe('facts');

// Update the current balance
updateBalance().then(async (bittrexBalances) => {
  const account = new Account();
  if (account.setBittrexBalance(bittrexBalances) > 1) {
    // Some crypto currency should have been sold to have this balance
    lastTrade = 'SELL-HIGH';
  } else {
    lastTrade = 'BUY';

    // We don't know the last price that you bought the security in your account for
    if (!Number.isNaN(parseFloat(process.argv[2]))) {
      // Get the price that was passed as a command line argument
      lastBuyPrice = parseFloat(process.argv[2]);
    } else {
      lastBuyPrice = (await getTicker(config.get('bittrexMarket'))).Ask; // Consider the current Ask price as the last buy price
    }

    log.info(`updateBalance, lastBuyPrice: ${lastBuyPrice}`);
  }
  log.info(`updateBalance, lastTrade: ${lastTrade}`);
  // TODO : Cancel all open orders when the script starts
});
<commit_msg>Transaction lock bug fixed<commit_after>const RuleEngine = require('node-rules');
const _ = require('lodash');
const config = require('config');
const redis = require('redis');
const getBalances = require('./lib/get-balances');
const Account = require('./lib/account');
const getTicker = require('./lib/get-ticker');
const getRange = require('./lib/get-range');
const getUpperSellPercentage = require('./lib/get-upper-sell-percentage');
const buyLimit = require('./lib/buy-limit');
const sellLimit = require('./lib/sell-limit');
const tradeStub = require('./lib/trade-stub');
const helper = require('./helper');
const poll = require('./poll');
const bunyan = require('bunyan');

const log = bunyan.createLogger({ name: 'siena' });

const redisClient = redis.createClient();
const redisClientForCacheOperations = redis.createClient();
const redisClientMessageQueue = redis.createClient();
redisClient.on('error', redisError => log.error(redisError));

let crossover;
let lastTrade;
let lastTradeTime = 0;
let lastBuyPrice = 0;
let lastSellPrice = 0;
let upperSellPercentage = config.get('strategy.upperSellPercentage');
let transactionLock = false;
const sienaAccount = new Account();

// Automation rules
const rules = [{
  condition: function condition(R) {
    R.when(_.has(this, 'netAssetValue') && (this.netAssetValue < config.get('sienaAccount.criticalPoint')));
  },
  consequence: function consequence(R) {
    this.actions = ['sellSecurity', 'halt'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'bittrexAccountBalances'));
  },
  consequence: function consequence(R) {
    this.actions = ['compartmentaliseAccount'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'movingAverageShort') &&
      _.has(this, 'movingAverageMid') &&
      _.has(this, 'movingAverageLong'));
  },
  consequence: function consequence(R) {
    this.actions = ['getMarketTrend'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'event') &&
      _.has(this, 'market') &&
      _.has(this, 'lastTrade') &&
      this.event === 'crossover' &&
      this.market === 'VOLATILE-LOW' &&
      this.lastTrade !== 'BUY');
  },
  consequence: function consequence(R) {
    // Buy security on the cheap as long as it isn't a bear market.
    this.actions = ['buySecurity'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'event') &&
      _.has(this, 'market') &&
      _.has(this, 'lastTrade') &&
      _.has(this, 'currentBidPrice') &&
      _.has(this, 'lastSellPrice') &&
      this.event === 'crossover' &&
      this.market !== 'BEAR' &&
      this.lastTrade === 'SELL-LOW' &&
      this.currentBidPrice < this.lastSellPrice);
  },
  consequence: function consequence(R) {
    // We've incurred a loss the last sale so buy it on the cheaper than your last sell price.
    this.actions = ['buySecurity'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'event') &&
      _.has(this, 'lastTrade') &&
      _.has(this, 'market') &&
      _.has(this, 'lastBuyPrice') &&
      _.has(this, 'currentBidPrice') &&
      _.has(this, 'rangePercentage') &&
      this.event === 'crossover' &&
      this.currentBidPrice > (this.lastBuyPrice + (upperSellPercentage * this.lastBuyPrice)) &&
      this.lastTrade === 'BUY' &&
      this.market !== 'BULL');
  },
  consequence: function consequence(R) {
    // You've got a profit so cash in!
    this.actions = ['sellSecurity'];
    R.stop();
  },
}, {
  condition: function condition(R) {
    R.when(_.has(this, 'lastTrade') &&
      _.has(this, 'market') &&
      _.has(this, 'lastBuyPrice') &&
      _.has(this, 'currentBidPrice') &&
      this.currentBidPrice < (this.lastBuyPrice - (config.get('strategy.lowerSellPercentage') * this.lastBuyPrice)) &&
      this.lastTrade === 'BUY' &&
      this.market === 'BEAR');
  },
  consequence: function consequence(R) {
    // This is a bear market, sell and wait for better buying opportunity.
    this.actions = ['sellSecurity'];
    R.stop();
  },
}];

const getMarketTrend = async (movingAverageShort, movingAverageMid, movingAverageLong) => {
  const currentMarket = {};
  if (movingAverageShort > movingAverageMid) {
    currentMarket.trend = 'UP';
  } else {
    currentMarket.trend = 'DOWN';
  }

  if (movingAverageShort >= movingAverageMid && movingAverageMid >= movingAverageLong) {
    currentMarket.market = 'BULL';
  } else if (movingAverageLong >= movingAverageMid && movingAverageMid >= movingAverageShort) {
    currentMarket.market = 'BEAR';
  } else if (movingAverageMid >= movingAverageShort && movingAverageShort >= movingAverageLong) {
    currentMarket.market = 'VOLATILE-MID';
  } else if (movingAverageLong >= movingAverageShort && movingAverageShort >= movingAverageMid) {
    currentMarket.market = 'VOLATILE-RECOVERY';
  } else if (movingAverageMid >= movingAverageLong && movingAverageLong >= movingAverageShort) {
    currentMarket.market = 'VOLATILE-LOW';
  } else if (movingAverageShort >= movingAverageLong && movingAverageLong >= movingAverageMid) {
    currentMarket.market = 'VOLATILE';
  } else {
    currentMarket.market = 'FLAT';
  }

  log.info(`getMarketTrend, trend : ${currentMarket.trend}, market: ${(currentMarket.market || 'nevermind')}`);

  if (crossover === undefined) {
    crossover = currentMarket;
  }

  let bearTicker;
  if (currentMarket.market === 'BEAR' && lastTrade === 'BUY' && lastBuyPrice > 0) {
    const bearFact = _.cloneDeep(currentMarket);
    bearFact.lastTrade = lastTrade;
    bearFact.lastBuyPrice = lastBuyPrice;

    bearTicker = await getTicker(config.get('bittrexMarket'));
    bearFact.currentBidPrice = bearTicker.Bid;

    // We need to publish this fact to the rules engine so that we can SELL
    // at the right time instead of waiting for a crossover moment.
    redisClientMessageQueue.publish('facts', JSON.stringify(bearFact));
  }

  if (_.isEqual(crossover, currentMarket)) {
    // The market has not crossedOver based on the last value
    return false;
  }

  // Market has crossedOver
  crossover = currentMarket;
  const fact = _.cloneDeep(crossover);
  fact.event = 'crossover';
  fact.crossoverTime = new Date().getTime();
  fact.lastTradeTime = lastTradeTime;
  fact.lastTrade = lastTrade;

  const tasks = [
    getRange(config.get('bittrexMarket')),
    bearTicker || getTicker(config.get('bittrexMarket')),
  ];

  const [range, ticker] = await Promise.all(tasks);
  fact.currentBidPrice = ticker.Bid;
  fact.rangePercentage = range / ticker.Bid;

  if (lastBuyPrice > 0) {
    fact.lastBuyPrice = lastBuyPrice;
  }

  if (lastSellPrice > 0) {
    fact.lastSellPrice = lastSellPrice;
  }

  log.info(`getMarketTrend, crossoverTime: ${fact.crossoverTime}`);

  redisClientMessageQueue.publish('facts', JSON.stringify(fact));

  // Cache crossover for strategy analysis
  // Cache recommendation
  const crossoverCacheData = {
    movingAverageShort,
    movingAverageMid,
    movingAverageLong,
    trend: currentMarket.trend,
    market: currentMarket.market,
    bidPrice: ticker.Bid,
    askPrice: ticker.Ask,
    timestamp: new Date(),
  };

  redisClientForCacheOperations.zadd([`${config.get('bittrexMarket')}-crossovers`, new Date().getTime(), `${JSON.stringify(crossoverCacheData)}`]);
  return currentMarket;
};

const compartmentaliseAccount = (bittrexAccountBalances) => {
  sienaAccount.setBittrexBalance(bittrexAccountBalances);
  redisClientMessageQueue.publish('facts', JSON.stringify({ accountBalance: sienaAccount.getBalanceNumber() }));
};

const updateBalance = async () => {
  const balances = await getBalances();
  redisClientMessageQueue.publish('facts', JSON.stringify({ bittrexAccountBalances: balances }));
  return balances;
};

const updateLastTradeTime = async (expectedBalance, action, price = undefined) => {
  const account = new Account();
  const balance = account.setBittrexBalance(await updateBalance());
  log.info(`updateLastTradeTime: actual balance:${balance}, expected balance: ${expectedBalance}.`);
  if (balance.toFixed(2) === expectedBalance.toFixed(2)) {
    if (action === 'BUY') {
      lastBuyPrice = price;

      // Calculate the SELL trigger prices
      upperSellPercentage = await getUpperSellPercentage(price);
      const upperBand = upperSellPercentage * parseFloat(lastBuyPrice);
      const lowerBand = config.get('strategy.lowerSellPercentage') * parseFloat(lastBuyPrice);
      const lowerSellTriggerPrice = parseFloat(lastBuyPrice) - lowerBand;
      const upperSellTriggerPrice = parseFloat(lastBuyPrice) + upperBand;
      log.info(`getMarketTrend, Upper SELL trigger price:${upperSellTriggerPrice}`);
      log.info(`getMarketTrend, Lower SELL trigger price:${lowerSellTriggerPrice}`);
    } else {
      lastSellPrice = price;
    }

    // trade was successful
    lastTradeTime = new Date().getTime();
    lastTrade = action;
    transactionLock = false;
    log.info(`updateLastTradeTime: lastTradeTime: ${lastTradeTime}, lastBuyPrice: ${(lastBuyPrice || 'nevermind')}`);
  } else {
    log.error('updateLastTradeTime, Error: lastTrade unsuccessful');

    // Why didn't bittrex fill the last order even after `balancePollInterval` seconds ?
    // Terminate Siena for now, investiage if your order was filled on bittrex and restart Siena

    // TODO : Maybe notify yourself that Siena has terminated due to an unfilled order
    process.exit();
  }
};

const buySecurity = async () => {
  if (config.get('trade') === false) {
    log.info('buySecurity, trade: false. Skipping security trades');
    return (false);
  }

  if (transactionLock) {
    log.info('buySecurity, transactionLock: true. Transaction in progress.');
    return (false);
  }

  transactionLock = false;
  const tasks = [
    getBalances(),
    getTicker(config.get('bittrexMarket')),
  ];
  const timeSinceLastTrade = new Date().getTime() - lastTradeTime;
  if (timeSinceLastTrade < config.get('balancePollInterval')) {
    log.warn(`buySecurity, timeSinceLastTrade: ${helper.millisecondsToHours(timeSinceLastTrade)}. Should maybe passing this buy signal?`);
  }

  const [bittrexBalances, ticker] = await Promise.all(tasks);
  sienaAccount.setBittrexBalance(bittrexBalances);
  if (sienaAccount.getBalanceNumber() < 1) {
    log.error(`buySecurity Error, account Balance : ${sienaAccount.getBalanceNumber()}. Not enough balance`);
    return (false);
  }

  const buyQuantity = sienaAccount.getTradeAmount() / ticker.Ask;
  const commission = tradeStub.getCommission(buyQuantity, ticker.Ask);
  const buyLesserQuantity = (sienaAccount.getTradeAmount() - commission) / ticker.Ask;

  log.info(`buySecurity: Buy ${buyLesserQuantity}${config.get('sienaAccount.securityCurrency')} for ${ticker.Ask} on ${new Date()}`);
  const order = await buyLimit(config.get('bittrexMarket'), buyLesserQuantity, ticker.Ask);
  log.info(`buySecurity, buyOrderUuid: ${order.uuid}`);
  const trade = tradeStub.buy(buyLesserQuantity, ticker.Ask);
  const expectedBalance = sienaAccount.getBalanceNumber() - trade.total;

  // Assume that this order gets filled and then update the balance
  setTimeout(() => { updateLastTradeTime(expectedBalance, 'BUY', ticker.Ask); }, config.get('balancePollInterval'));
  return (true);
};

const sellSecurity = async () => {
  if (config.get('trade') === false) {
    log.info('sellSecurity, trade: false. Skipping security trades');
    return (false);
  }

  if (transactionLock) {
    log.info('sellSecurity, transactionLock: true. Transaction in progress.');
  }

  transactionLock = false;

  const tasks = [
    getBalances(),
    getTicker(config.get('bittrexMarket')),
  ];

  let ticker;
  let bittrexBalances;
  try {
    [bittrexBalances, ticker] = await Promise.all(tasks);
  } catch (err) {
    log.error(`sellSecurity, Error : ${err}`);
    return (false);
  }

  sienaAccount.setBittrexBalance(bittrexBalances);

  const securityQuantity = sienaAccount.getBittrexBalance();
  if (securityQuantity > 0) {
    log.info(`sellSecurity: Sell ${securityQuantity}${config.get('sienaAccount.securityCurrency')} for ${ticker.Bid}`);
    const order = await sellLimit(config.get('bittrexMarket'), securityQuantity, ticker.Bid);
    log.info(`sellSecurity, sellOrderUuid: ${order.uuid}`);
    const trade = tradeStub.sell(securityQuantity, ticker.Bid);
    const expectedBalance = sienaAccount.getBalanceNumber() + trade.total;

    // Assume that this order gets filled and then update the balance
    setTimeout(() => {
      updateLastTradeTime(expectedBalance,
        (parseFloat(ticker.Bid) > parseFloat(lastBuyPrice) ? 'SELL-HIGH' : 'SELL-LOW'),
        ticker.Bid);
    }, config.get('balancePollInterval'));
    return (true);
  }

  log.error('sellSecurity Error: No security to Sell');
  return (false);
};

// initialize the rule engine
const R = new RuleEngine(rules);

redisClient.on('message', (channel, message) => {
  try {
    const fact = JSON.parse(message);

    // Pass the fact on to the rule engine for results
    R.execute(fact, (result) => {
      log.info(`Siena Rules : fact : ${JSON.stringify(fact)}`);

      if (_.includes(result.actions, 'infer') && _.has(result, 'fact')) {
        redisClientMessageQueue.publish('facts', JSON.stringify(result.fact));
      }

      if (_.includes(result.actions, 'getMarketTrend')
          && _.has(result, 'movingAverageShort')
          && _.has(result, 'movingAverageMid')
          && _.has(result, 'movingAverageLong')) {
        getMarketTrend(result.movingAverageShort,
          result.movingAverageMid, result.movingAverageLong);
      }

      if (_.includes(result.actions, 'compartmentaliseAccount') && _.has(result, 'bittrexAccountBalances')) {
        compartmentaliseAccount(result.bittrexAccountBalances);
      }

      if (_.includes(result.actions, 'buySecurity')) {
        buySecurity();
      }

      if (_.includes(result.actions, 'sellSecurity')) {
        sellSecurity();
      }
    });
  } catch (error) {
    log.error('Siena Rules : Error : ', error);
  }
});

// Keep polling the moving averages
setInterval(() => poll(config.get('bittrexMarket')), 5000);

// Listen for facts
redisClient.subscribe('facts');

// Update the current balance
updateBalance().then(async (bittrexBalances) => {
  const account = new Account();
  if (account.setBittrexBalance(bittrexBalances) > 1) {
    // Some crypto currency should have been sold to have this balance
    lastTrade = 'SELL-HIGH';
  } else {
    lastTrade = 'BUY';

    // We don't know the last price that you bought the security in your account for
    if (!Number.isNaN(parseFloat(process.argv[2]))) {
      // Get the price that was passed as a command line argument
      lastBuyPrice = parseFloat(process.argv[2]);
    } else {
      lastBuyPrice = (await getTicker(config.get('bittrexMarket'))).Ask; // Consider the current Ask price as the last buy price
    }

    log.info(`updateBalance, lastBuyPrice: ${lastBuyPrice}`);
  }
  log.info(`updateBalance, lastTrade: ${lastTrade}`);
  // TODO : Cancel all open orders when the script starts
});
<|endoftext|>


<commit_before>/* eslint-disable no-undef */
import Utils from './utils';

class CoconutCalendar {
  constructor(path, params = {}, width = '100%', height = '580px') {
    this.path = path;
    this.width = width;
    this.height = height;
    this.params = Utils.parameterize(params);
    this.iframe = document.createElement('iframe');

    this.iframe.setAttribute('seamless', true);
    this.iframe.setAttribute('frameborder', 0);
    this.iframe.setAttribute('width', this.width);
    this.iframe.setAttribute('height', this.height);

    const url = Utils.isFullUrl(this.path) ? `${this.path}/embed` : `https://${this.path}.coconutcalendar.com/embed`;
    this.iframe.setAttribute('src', `${url}${this.params}`);
  }

  embed(id) {
    const div = document.getElementById(id);

    div.appendChild(this.iframe);
  }
}

module.exports = CoconutCalendar;
<commit_msg>Adds geolocation allowance to iframe generation<commit_after>/* eslint-disable no-undef */
import Utils from './utils';

class CoconutCalendar {
  constructor(path, params = {}, width = '100%', height = '580px') {
    this.path = path;
    this.width = width;
    this.height = height;
    this.params = Utils.parameterize(params);
    this.iframe = document.createElement('iframe');

    this.iframe.setAttribute('seamless', true);
    this.iframe.setAttribute('frameborder', 0);
    this.iframe.setAttribute('allow', 'geolocation');
    this.iframe.setAttribute('width', this.width);
    this.iframe.setAttribute('height', this.height);

    const url = Utils.isFullUrl(this.path) ? `${this.path}/embed` : `https://${this.path}.coconutcalendar.com/embed`;
    this.iframe.setAttribute('src', `${url}${this.params}`);
  }

  embed(id) {
    const div = document.getElementById(id);

    div.appendChild(this.iframe);
  }
}

module.exports = CoconutCalendar;
<|endoftext|>


<commit_before>'use strict';

var Equilibrium = require('chem-equilibrium');
var parser = require('./parser');
var cache = require('./cc-cache');

function EasyEq(desc) {
    this.desc = desc;
    this._init();
    this._components();
    this._species();
    this._charges();
    this._coefficients();
    this._concentrations();
    this._constants();
    this._precipitationCoefficients();
    this._precipitationConstants();

    //
    this.equilibrium = new Equilibrium({
        precipitationConstants: this.precipitationConstants || [],
        precipitationCoefficients: this.precipitationCoefficients || [[]],
        solutionConstants: this.constants,
        solutionSpeciesCharges: this.charges,
        solutionCoefficients: this.coefficients,
        totalConcentrations: this.totalConcentrations,
        fixedActivities: this.fixedConcentrations,
        randomGenerator: this.desc.randomGenerator,
        maxGuesses: this.desc.maxGuesses
    });
}

EasyEq.prototype._init = function() {
    // Check fixed parameter
    this.desc.fixed = this.desc.fixed || [];
    if(this.desc.fixed === 'none') this.desc.fixed = [];
    if(!Array.isArray(this.desc.fixed)) {
        this.desc.fixed = [this.desc.fixed];
    }

    this.Ka = {};
    this.equationsHash = {};
    var i, rname;
    this.equations = this.desc.eq.filter(function(v, i) {
        return i%2 === 0;
    });

    var constants = this.desc.eq.filter(function(v, i) {
        return i%2 === 1;
    });
    for(i=0; i<this.equations.length; i++) {
        rname = cache.norm(parser.parseReaction(this.equations[i]).reactives[0].c);
        this.Ka[rname] = constants[i];
        this.equationsHash[rname] = this.equations[i];
    }

    if(!this.desc.precipitations) return;

    this.precipitationConstants = this.desc.precipitations.filter(function(v, i) {
        return i%2 === 1;
    });
    this.precipitations = this.desc.precipitations.filter(function(v,i){
        return i%2 === 0;
    });

    for(i=0; i<this.precipitations.length; i++) {
        rname = cache.norm(parser.parseReaction(this.precipitations[i]).reactives[0].c);
    }
};

EasyEq.prototype._components = function() {
    this.components = [];
    for(var i=0; i<this.equations.length; i++) {

        var parsed = parser.parseReaction(this.equations[i]);
        if(parsed.reactives.length !== 1 || parsed.products.length !== 1) continue;
        var rname = cache.norm(parsed.reactives[0].c);
        var pname = cache.norm(parsed.products[0].c);
        if(rname !== pname) {
            console.log('continue');
            continue;
        }
        this.components.push(rname);
    }

    // Order components
    // Find fixed component
    var idx;
    if(this.desc.fixed) {
        for(var j=0; j<this.desc.fixed.length; j++) {
            var fixed = cache.norm(this.desc.fixed[j]);
            idx = this.components.indexOf(fixed);
            if(idx > -1) {
                this.components.splice(idx,1);
                this.components.push(fixed);
            }
        }

    }

    if(this.desc.solvent) {
        var solvent = cache.norm(this.desc.solvent);
        idx = this.components.indexOf(solvent);
        if(idx > -1) {
            this.components.splice(idx, 1);
            this.components.push(solvent);
        }
    }
    console.log('components', this.components);
};

EasyEq.prototype._species = function() {
    this.species = new Array(this.components.length);
    for(var i=0; i<this.components.length; i++) this.species[i] = this.components[i];

    for(i=0; i<this.equations.length; i++) {
        var parsed = parser.parseReaction(this.equations[i]);
        var rname = cache.norm(parsed.reactives[0].c);
        if(this.species.indexOf(rname) === -1) this.species.push(rname);
    }
    console.log('species', this.species);
};

EasyEq.prototype._charges = function() {
    this.charges = [];
    for(var i=0; i<this.species.length; i++) {
        var totalCharge = 0;
        var cc = cache.getCC(this.species[i]);
        for (var j = 0; j < cc.parts.length; j++) {
            totalCharge += (cc.parts[j].charge || 0);
        }
        this.charges.push(totalCharge);
    }
    console.log('charges', this.charges);
};

EasyEq.prototype._coefficients = function() {
    this.coefficients = new Array(this.species.length);
    for(var i=0; i<this.species.length; i++) {
        this.coefficients[i] = new Array(this.components.length);
        fillArray(this.coefficients[i], 0);
    }

    for(var key in this.equationsHash) {
        var idx = this.species.indexOf(key);
        if(idx === -1) throw new Error('Cannot find specie');
        var parsed = parser.parseReaction(this.equationsHash[key]);

        // Rewrite products as linear combinations of components
        var products = this._rebase(parsed.products);
        //var products = parsed.products;
        for(i=0; i<products.length; i++) {
            var product = products[i];
            var cidx = this.components.indexOf(cache.norm(product.c));
            if(cidx === -1) {
                console.log(this.components);
                throw new Error('Cannot find component ' + product.c);

            }
            this.coefficients[idx][cidx] += product.n;
        }
    }
    console.log('coefficients', this.coefficients);
};

EasyEq.prototype._rebase = function(elements) {
    var r = [];
    for(var i=0; i<elements.length; i++) {
        var replaced = false;
        var idx = this.components.indexOf(elements[i].c);
        var idxs = this.species.indexOf(elements[i].c);
        if(idx === -1 && idxs > -1) {
            var eq = this.equationsHash[this.species[idxs]];
            if(eq) {
                var parsed = parser.parseReaction(eq);
                for(var j=0; j<parsed.products.length; j++) {
                    parsed.products[j].n *= elements[i].n;
                    r.push(parsed.products[j]);
                }
                replaced = true;
            }
        }
        if(!replaced) {
            r.push(elements[i]);
        }
    }
    return r;
};

EasyEq.prototype._precipitationCoefficients = function() {
    if(!this.desc.precipitations) return;
    this.precipitationCoefficients = new Array(this.precipitations.length);
    for(var i=0; i<this.precipitations.length; i++) {
        this.precipitationCoefficients[i] = new Array(this.components.length);
        fillArray(this.precipitationCoefficients[i], 0);
    }

    for(var j=0; j<this.precipitations.length; j++) {
        var parsed = parser.parseReaction(this.precipitations[j]);
        var products = this._rebase(parsed.products);
        for(i=0; i<products.length; i++) {
            var product = products[i];
            var cidx = this.components.indexOf(cache.norm(product.c));
            if(cidx === -1) {
                console.log(this.components);
                throw new Error('Cannot find component ' + product.c);
            }
            this.precipitationCoefficients[j][cidx] += product.n;
        }
    }
    console.log('precipitation coefficients', this.precipitationCoefficients);
};

EasyEq.prototype._precipitationConstants = function() {
      if(!this.desc.precipitations) return;
    for(var i=0;i<this.precipitationConstants.length; i++) {
        // this.precipitationConstants[i] = 1/this.precipitationConstants[i];
    }
    console.log('precipitation constants', this.precipitationConstants);
 };

EasyEq.prototype._concentrations = function() {
    this.totalConcentrations = [];
    this.fixedConcentrations = [];

    for(var i=0; i<this.components.length; i++) {
        if(this.isFixed(this.components[i])) {
            this.fixedConcentrations.push(this.desc.concentrations[this.components[i]] || 0);
            continue;
        }
        if(this.isSolvent(this.components[i])) continue;
        this.totalConcentrations.push(this.desc.concentrations[this.components[i]]);
    }
    console.log('total concentrations', this.totalConcentrations);
    console.log('fixed concentrations', this.fixedConcentrations);
};

EasyEq.prototype._constants = function() {
    this.constants = new Array(this.species.length);
    for(var key in this.Ka) {
        var idx = this.species.indexOf(key);
        if(idx === -1) throw new Error('Cannot link ka to specie');
        this.constants[idx] = 1/this.Ka[key];
    }
    console.log('constants', this.constants);
};

EasyEq.prototype.isSolvent = function(c) {
    if(this.desc.solvent === undefined || this.desc.solvent === 'none'){
        return false;
    }
    var c1 = cache.norm(this.desc.solvent);
    var c2 = cache.norm(c);
    return c1 === c2;
};

EasyEq.prototype.isFixed = function(c) {
    var c1,c2;
    for(var i=0; i<this.desc.fixed.length; i++) {
        c1 = cache.norm(this.desc.fixed[i]);
        c2 = cache.norm(c);
        if(c1 === c2) return true;
    }
    return false;
};

EasyEq.prototype.hasFixed = function() {
    return this.desc.fixed !== undefined && this.desc.fixed !== 'none';
};

EasyEq.prototype.hasSolvent = function() {
    return this.desc.solvent !== undefined && this.desc.solvent !== 'none';
};

EasyEq.prototype.setEquilibriumActivity = function(c, concentration) {
    c = cache.norm(c);
    var idx = this.components.indexOf(c);
    if(idx === -1) throw new Error('Cannot set equilibrium activity of non-components');
    return this.equilibrium.setEquilibriumActivity(idx, concentration);
};

EasyEq.prototype.setTotalConcentration = function(c, concentration) {
    c = cache.norm(c);
    var idx = this.components.indexOf(c);
    if(idx === -1) throw new Error('Cannot set total concentration of non-components');
    return this.equilibrium.setTotalConcentration(idx, concentration);
};

EasyEq.prototype.calculate = function() {
    this.equilibrium.calculate.apply(this.equilibrium, arguments);
};

EasyEq.prototype.getSpeciesConcentrations = function() {
    var c = this.equilibrium.getSolutionSpeciesConcentrations();
    console.log(c.length);
    var r = {};
    for(var i=0; i< c.length; i++) {
        r[this.species[i]] = c[i];
    }
    return r;
};

EasyEq.prototype.saturationIndices = function() {
    return this.equilibrium.saturationIndices();
};

EasyEq.prototype.finalConcentrations = function() {
    return this.equilibrium.finalConcentrations();
}

function fillArray(arr, val) {
    for(var i=0; i<arr.length; i++) {
        arr[i] = val;
    }
}

module.exports = EasyEq;<commit_msg>Remove logs<commit_after>'use strict';

var Equilibrium = require('chem-equilibrium');
var parser = require('./parser');
var cache = require('./cc-cache');

function EasyEq(desc) {
    this.desc = desc;
    this._init();
    this._components();
    this._species();
    this._charges();
    this._coefficients();
    this._concentrations();
    this._constants();
    this._precipitationCoefficients();
    this._precipitationConstants();

    //
    this.equilibrium = new Equilibrium({
        precipitationConstants: this.precipitationConstants || [],
        precipitationCoefficients: this.precipitationCoefficients || [[]],
        solutionConstants: this.constants,
        solutionSpeciesCharges: this.charges,
        solutionCoefficients: this.coefficients,
        totalConcentrations: this.totalConcentrations,
        fixedActivities: this.fixedConcentrations,
        randomGenerator: this.desc.randomGenerator,
        maxGuesses: this.desc.maxGuesses
    });
}

EasyEq.prototype._init = function() {
    // Check fixed parameter
    this.desc.fixed = this.desc.fixed || [];
    if(this.desc.fixed === 'none') this.desc.fixed = [];
    if(!Array.isArray(this.desc.fixed)) {
        this.desc.fixed = [this.desc.fixed];
    }

    this.Ka = {};
    this.equationsHash = {};
    var i, rname;
    this.equations = this.desc.eq.filter(function(v, i) {
        return i%2 === 0;
    });

    var constants = this.desc.eq.filter(function(v, i) {
        return i%2 === 1;
    });
    for(i=0; i<this.equations.length; i++) {
        rname = cache.norm(parser.parseReaction(this.equations[i]).reactives[0].c);
        this.Ka[rname] = constants[i];
        this.equationsHash[rname] = this.equations[i];
    }

    if(!this.desc.precipitations) return;

    this.precipitationConstants = this.desc.precipitations.filter(function(v, i) {
        return i%2 === 1;
    });
    this.precipitations = this.desc.precipitations.filter(function(v,i){
        return i%2 === 0;
    });

    for(i=0; i<this.precipitations.length; i++) {
        rname = cache.norm(parser.parseReaction(this.precipitations[i]).reactives[0].c);
    }
};

EasyEq.prototype._components = function() {
    this.components = [];
    for(var i=0; i<this.equations.length; i++) {

        var parsed = parser.parseReaction(this.equations[i]);
        if(parsed.reactives.length !== 1 || parsed.products.length !== 1) continue;
        var rname = cache.norm(parsed.reactives[0].c);
        var pname = cache.norm(parsed.products[0].c);
        if(rname !== pname) {
            continue;
        }
        this.components.push(rname);
    }

    // Order components
    // Find fixed component
    var idx;
    if(this.desc.fixed) {
        for(var j=0; j<this.desc.fixed.length; j++) {
            var fixed = cache.norm(this.desc.fixed[j]);
            idx = this.components.indexOf(fixed);
            if(idx > -1) {
                this.components.splice(idx,1);
                this.components.push(fixed);
            }
        }

    }

    if(this.desc.solvent) {
        var solvent = cache.norm(this.desc.solvent);
        idx = this.components.indexOf(solvent);
        if(idx > -1) {
            this.components.splice(idx, 1);
            this.components.push(solvent);
        }
    }
};

EasyEq.prototype._species = function() {
    this.species = new Array(this.components.length);
    for(var i=0; i<this.components.length; i++) this.species[i] = this.components[i];

    for(i=0; i<this.equations.length; i++) {
        var parsed = parser.parseReaction(this.equations[i]);
        var rname = cache.norm(parsed.reactives[0].c);
        if(this.species.indexOf(rname) === -1) this.species.push(rname);
    }
};

EasyEq.prototype._charges = function() {
    this.charges = [];
    for(var i=0; i<this.species.length; i++) {
        var totalCharge = 0;
        var cc = cache.getCC(this.species[i]);
        for (var j = 0; j < cc.parts.length; j++) {
            totalCharge += (cc.parts[j].charge || 0);
        }
        this.charges.push(totalCharge);
    }
};

EasyEq.prototype._coefficients = function() {
    this.coefficients = new Array(this.species.length);
    for(var i=0; i<this.species.length; i++) {
        this.coefficients[i] = new Array(this.components.length);
        fillArray(this.coefficients[i], 0);
    }

    for(var key in this.equationsHash) {
        var idx = this.species.indexOf(key);
        if(idx === -1) throw new Error('Cannot find specie');
        var parsed = parser.parseReaction(this.equationsHash[key]);

        // Rewrite products as linear combinations of components
        var products = this._rebase(parsed.products);
        //var products = parsed.products;
        for(i=0; i<products.length; i++) {
            var product = products[i];
            var cidx = this.components.indexOf(cache.norm(product.c));
            if(cidx === -1) {
                throw new Error('Cannot find component ' + product.c);

            }
            this.coefficients[idx][cidx] += product.n;
        }
    }
};

EasyEq.prototype._rebase = function(elements) {
    var r = [];
    for(var i=0; i<elements.length; i++) {
        var replaced = false;
        var idx = this.components.indexOf(elements[i].c);
        var idxs = this.species.indexOf(elements[i].c);
        if(idx === -1 && idxs > -1) {
            var eq = this.equationsHash[this.species[idxs]];
            if(eq) {
                var parsed = parser.parseReaction(eq);
                for(var j=0; j<parsed.products.length; j++) {
                    parsed.products[j].n *= elements[i].n;
                    r.push(parsed.products[j]);
                }
                replaced = true;
            }
        }
        if(!replaced) {
            r.push(elements[i]);
        }
    }
    return r;
};

EasyEq.prototype._precipitationCoefficients = function() {
    if(!this.desc.precipitations) return;
    this.precipitationCoefficients = new Array(this.precipitations.length);
    for(var i=0; i<this.precipitations.length; i++) {
        this.precipitationCoefficients[i] = new Array(this.components.length);
        fillArray(this.precipitationCoefficients[i], 0);
    }

    for(var j=0; j<this.precipitations.length; j++) {
        var parsed = parser.parseReaction(this.precipitations[j]);
        var products = this._rebase(parsed.products);
        for(i=0; i<products.length; i++) {
            var product = products[i];
            var cidx = this.components.indexOf(cache.norm(product.c));
            if(cidx === -1) {
                throw new Error('Cannot find component ' + product.c);
            }
            this.precipitationCoefficients[j][cidx] += product.n;
        }
    }
};

EasyEq.prototype._precipitationConstants = function() {
      if(!this.desc.precipitations) return;
    for(var i=0;i<this.precipitationConstants.length; i++) {
        // this.precipitationConstants[i] = 1/this.precipitationConstants[i];
    }
 };

EasyEq.prototype._concentrations = function() {
    this.totalConcentrations = [];
    this.fixedConcentrations = [];

    for(var i=0; i<this.components.length; i++) {
        if(this.isFixed(this.components[i])) {
            this.fixedConcentrations.push(this.desc.concentrations[this.components[i]] || 0);
            continue;
        }
        if(this.isSolvent(this.components[i])) continue;
        this.totalConcentrations.push(this.desc.concentrations[this.components[i]]);
    }
};

EasyEq.prototype._constants = function() {
    this.constants = new Array(this.species.length);
    for(var key in this.Ka) {
        var idx = this.species.indexOf(key);
        if(idx === -1) throw new Error('Cannot link ka to specie');
        this.constants[idx] = 1/this.Ka[key];
    }
};

EasyEq.prototype.isSolvent = function(c) {
    if(this.desc.solvent === undefined || this.desc.solvent === 'none'){
        return false;
    }
    var c1 = cache.norm(this.desc.solvent);
    var c2 = cache.norm(c);
    return c1 === c2;
};

EasyEq.prototype.isFixed = function(c) {
    var c1,c2;
    for(var i=0; i<this.desc.fixed.length; i++) {
        c1 = cache.norm(this.desc.fixed[i]);
        c2 = cache.norm(c);
        if(c1 === c2) return true;
    }
    return false;
};

EasyEq.prototype.hasFixed = function() {
    return this.desc.fixed !== undefined && this.desc.fixed !== 'none';
};

EasyEq.prototype.hasSolvent = function() {
    return this.desc.solvent !== undefined && this.desc.solvent !== 'none';
};

EasyEq.prototype.setEquilibriumActivity = function(c, concentration) {
    c = cache.norm(c);
    var idx = this.components.indexOf(c);
    if(idx === -1) throw new Error('Cannot set equilibrium activity of non-components');
    return this.equilibrium.setEquilibriumActivity(idx, concentration);
};

EasyEq.prototype.setTotalConcentration = function(c, concentration) {
    c = cache.norm(c);
    var idx = this.components.indexOf(c);
    if(idx === -1) throw new Error('Cannot set total concentration of non-components');
    return this.equilibrium.setTotalConcentration(idx, concentration);
};

EasyEq.prototype.calculate = function() {
    this.equilibrium.calculate.apply(this.equilibrium, arguments);
};

EasyEq.prototype.getSpeciesConcentrations = function() {
    var c = this.equilibrium.getSolutionSpeciesConcentrations();
    var r = {};
    for(var i=0; i< c.length; i++) {
        r[this.species[i]] = c[i];
    }
    return r;
};

EasyEq.prototype.saturationIndices = function() {
    return this.equilibrium.saturationIndices();
};

EasyEq.prototype.finalConcentrations = function() {
    return this.equilibrium.finalConcentrations();
};

function fillArray(arr, val) {
    for(var i=0; i<arr.length; i++) {
        arr[i] = val;
    }
}

module.exports = EasyEq;<|endoftext|>


<commit_before>import React, { Component } from 'react'
// import logo from './logo.svg';
import './style.css'
import Canvas from '../Canvas'

class App extends Component {
  render () {
    return (

Snake - React - v4

        <Canvas />

    )
  }
}

export default App
<commit_msg>code version 5.0<commit_after>import React, { Component } from 'react'
// import logo from './logo.svg';
import './style.css'
import Canvas from '../Canvas'

class App extends Component {
  render () {
    return (

Snake - React - v5

        <Canvas />

    )
  }
}

export default App
<|endoftext|>


<commit_before>// Return Promise
const imageMerge = (sources = [], options = {format: 'image/png'}) => new Promise(resolve => {
	// Setup browser/node specific variables
	const canvas = options.Canvas ? new options.Canvas() : window.document.createElement('canvas');
	const Image = options.Canvas ? options.Canvas.Image : window.Image;

	// Load sources
	const images = sources.map(source => new Promise(resolve => {
		// Convert strings to objects
		if (typeof source === 'string') {
			source = {src: source};
		}

		// Resolve source and img when loaded
		const img = new Image();
		img.onload = () => resolve(Object.assign({}, source, {img}));
		img.src = source.src;
	}));

	// Get canvas context
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			const getSize = dim => options[dim] || Math.max(...images.map(image => image.img[dim]));
			canvas.width = getSize('width');
			canvas.height = getSize('height');

			// Draw images to canvas
			images.forEach(image => ctx.drawImage(image.img, image.x || 0, image.y || 0));

			// Resolve data uri
			resolve(canvas.toDataURL(options.format));
		});
});

module.exports = imageMerge;
<commit_msg>imageMerge => mergeImages<commit_after>// Return Promise
const mergeImages = (sources = [], options = {format: 'image/png'}) => new Promise(resolve => {
	// Setup browser/node specific variables
	const canvas = options.Canvas ? new options.Canvas() : window.document.createElement('canvas');
	const Image = options.Canvas ? options.Canvas.Image : window.Image;

	// Load sources
	const images = sources.map(source => new Promise(resolve => {
		// Convert strings to objects
		if (typeof source === 'string') {
			source = {src: source};
		}

		// Resolve source and img when loaded
		const img = new Image();
		img.onload = () => resolve(Object.assign({}, source, {img}));
		img.src = source.src;
	}));

	// Get canvas context
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			const getSize = dim => options[dim] || Math.max(...images.map(image => image.img[dim]));
			canvas.width = getSize('width');
			canvas.height = getSize('height');

			// Draw images to canvas
			images.forEach(image => ctx.drawImage(image.img, image.x || 0, image.y || 0));

			// Resolve data uri
			resolve(canvas.toDataURL(options.format));
		});
});

module.exports = mergeImages;
<|endoftext|>


<commit_before>const productionHost = 'buy.itunes.apple.com';
const sandboxHost = 'sandbox.itunes.apple.com';

const statusCodes = {
  [0]:     { message:'Active', valid: true, error: false },
  [21000]: { message:'App store could not read', valid: false, error: true },
  [21002]: { message:'Data was malformed', valid: false, error: true },
  [21003]: { message:'Receipt not authenticated', valid: false, error: true },
  [21004]: { message:'Shared secret does not match', valid: false, error: true },
  [21005]: { message:'Receipt server unavailable', valid: false, error: true },
  [21006]: { message:'Receipt valid but sub expired', valid: false, error: false },
  [21007]: { message:'Sandbox receipt sent to Production environment', valid: false, error: true, redirect: true }, // special case for app review handling - forward any request that is intended for the Sandbox but was sent to Production, this is what the app review team does
  [21008]: { message:'Production receipt sent to Sandbox environment', valid: false, error: true },
};

function VerificationError(error) {
  ['message', 'valid', 'error', 'redirect'].map(prop => this[prop] = error[prop]);
}
VerificationError.prototype = Object.create(Error.prototype);

export default function(password, production=true) {
  const endpoint = production ? productionHost : sandboxHost;
  const verifyUrl = `https://${endpoint}/verifyReceipt`;

  return async (receipt) => {
    const payload = {
      'receipt-data': receipt + 'test',
      password,
    };

    const options = {
      body: JSON.stringify(payload),
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    };

    const res = await fetch(verifyUrl, options);
    const body = await res.json();

    if (body.status !== 0) {
      throw new VerificationError(statusCodes[body.status]);
    }

    return receipt;
  }
}

<commit_msg>Remove test from receipt-data<commit_after>const productionHost = 'buy.itunes.apple.com';
const sandboxHost = 'sandbox.itunes.apple.com';

const statusCodes = {
  [0]:     { message:'Active', valid: true, error: false },
  [21000]: { message:'App store could not read', valid: false, error: true },
  [21002]: { message:'Data was malformed', valid: false, error: true },
  [21003]: { message:'Receipt not authenticated', valid: false, error: true },
  [21004]: { message:'Shared secret does not match', valid: false, error: true },
  [21005]: { message:'Receipt server unavailable', valid: false, error: true },
  [21006]: { message:'Receipt valid but sub expired', valid: false, error: false },
  [21007]: { message:'Sandbox receipt sent to Production environment', valid: false, error: true, redirect: true }, // special case for app review handling - forward any request that is intended for the Sandbox but was sent to Production, this is what the app review team does
  [21008]: { message:'Production receipt sent to Sandbox environment', valid: false, error: true },
};

function VerificationError(error) {
  ['message', 'valid', 'error', 'redirect'].map(prop => this[prop] = error[prop]);
}
VerificationError.prototype = Object.create(Error.prototype);

export default function(password, production=true) {
  const endpoint = production ? productionHost : sandboxHost;
  const verifyUrl = `https://${endpoint}/verifyReceipt`;

  return async (receipt) => {
    const payload = {
      'receipt-data': receipt,
      password,
    };

    const options = {
      body: JSON.stringify(payload),
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    };

    const res = await fetch(verifyUrl, options);
    const body = await res.json();

    if (body.status !== 0) {
      throw new VerificationError(statusCodes[body.status]);
    }

    return receipt;
  }
}

<|endoftext|>


<commit_before>import {extras, spy} from 'mobx';
import React from 'react';
import {unstable_batchedUpdates as rdBatched} from 'react-dom';
import {unstable_batchedUpdates as rnBatched} from 'react-native';

let TARGET_LIB_NAME;
if (__TARGET__ === 'browser') TARGET_LIB_NAME = 'mobx-react';
if (__TARGET__ === 'native') TARGET_LIB_NAME = 'mobx-react/native';
if (__TARGET__ === 'custom') TARGET_LIB_NAME = 'mobx-react/custom';

if (!React)
  throw new Error(TARGET_LIB_NAME + ' requires React to be available');

if (__TARGET__ === 'browser' && typeof rdBatched === "function")
  extras.setReactionScheduler(rdBatched);
if (__TARGET__ === 'native' && typeof rnBatched === "function")
  extras.setReactionScheduler(rnBatched);

export {
    observer,
    Observer,
    renderReporter,
    componentByNodeRegistery,
    trackComponents,
    useStaticRendering
} from "./observer"

export { default as Provider } from "./Provider"
export { default as inject } from "./inject"

import * as propTypes from "./propTypes"
export { propTypes }
export { propTypes as PropTypes }

import { errorsReporter } from "./observer"
export const onError = fn => errorsReporter.on(fn)

export default exports

/* DevTool support */
import { renderReporter, componentByNodeRegistery, trackComponents } from './observer';
if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === 'object') {
  const mobx = { spy, extras };
  const mobxReact = { renderReporter, componentByNodeRegistery, trackComponents };
  __MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobxReact(mobxReact, mobx);
}
<commit_msg>Removed forgotten * import<commit_after>import {extras, spy} from 'mobx';
import { Component } from 'react';
import {unstable_batchedUpdates as rdBatched} from 'react-dom';
import {unstable_batchedUpdates as rnBatched} from 'react-native';

let TARGET_LIB_NAME;
if (__TARGET__ === 'browser') TARGET_LIB_NAME = 'mobx-react';
if (__TARGET__ === 'native') TARGET_LIB_NAME = 'mobx-react/native';
if (__TARGET__ === 'custom') TARGET_LIB_NAME = 'mobx-react/custom';

if (!Component)
  throw new Error(TARGET_LIB_NAME + ' requires React to be available');

if (__TARGET__ === 'browser' && typeof rdBatched === "function")
  extras.setReactionScheduler(rdBatched);
if (__TARGET__ === 'native' && typeof rnBatched === "function")
  extras.setReactionScheduler(rnBatched);

export {
    observer,
    Observer,
    renderReporter,
    componentByNodeRegistery,
    trackComponents,
    useStaticRendering
} from "./observer"

export { default as Provider } from "./Provider"
export { default as inject } from "./inject"

import * as propTypes from "./propTypes"
export { propTypes }
export { propTypes as PropTypes }

import { errorsReporter } from "./observer"
export const onError = fn => errorsReporter.on(fn)

export default exports

/* DevTool support */
import { renderReporter, componentByNodeRegistery, trackComponents } from './observer';
if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === 'object') {
  const mobx = { spy, extras };
  const mobxReact = { renderReporter, componentByNodeRegistery, trackComponents };
  __MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobxReact(mobxReact, mobx);
}
<|endoftext|>


<commit_before>
import Marcheur from 'marcheur';
import nodal from 'marcheur/lib/nodal';
import qname from 'marcheur/lib/qname';
import Matcher from 'marcheur/lib/matcher';
import dom from 'get-dom';
import xpath from 'xpath';
import { oprx } from './operators';

let MATH_NS = 'http://www.w3.org/1998/Math/MathML'
  , nsMap = {
      m:  'http://schemas.openxmlformats.org/officeDocument/2006/math',
    }
  , select = xpath.useNamespaces(nsMap)
  , selectAttr = (path, attr, ctx) => {
      let el = select(path, ctx)[0];
      if (!el) return '';
      let atn = qname(attr, nsMap);
      if (atn.ns) return el.getAttributeNS(atn.ns, atn.ln);
      return el.getAttribute(atn.qn);
    }
  , el
;

export default function omml2mathml (omml) {
  let m = new Matcher(nsMap)
    , walker = new Marcheur()
  ;
  return walker
    // #document
    .match(m.document(),
      (src, out, w) => {
        let doc = dom.implementation().createHTMLDocument('')
          , nod = nodal(doc, {}, nsMap)
          , math = doc.createElementNS(MATH_NS, 'math')
        ;
        math.setAttribute('display', 'inline');
        el = nod.el;
        w.result(math);
        w.walk(math);
      }
    )
    .match(
      m.el('m:oMathPara'),
      (src, out, w) => {
        w.res.setAttribute('display', 'block');
        w.walk(out);
      }
    )
    .match(
      m.el('m:oMath'),
      (src, out, w) => w.walk(out)
    )
    .match(
      m.el('m:f'),
      (src, out, w) => {
        let type = (selectAttr('./m:fPr[last()]/m:type', 'm:val', src) || '').toLowerCase()
          , outer = (type === 'lin')
                      ? el('mrow', {}, out)
                      : el('mfrac', fracProp(type), out)
        ;
        let numRow = el('mrow', {}, outer);
        w.walk(numRow, select('m:num[1]', src));
        if (type === 'lin') {
          let mo = el('mo', {}, outer);
          mo.textContent = '/';
        }
        let denRow = el('mrow', {}, outer);
        w.walk(denRow, select('m:den[1]', src));
      }
    )
    .match(
      m.el('m:r'),
      (src, out) => {
        let nor = selectAttr('m:rPr[last()]/m:nor', 'm:val', src);
        if (!nor) nor = false;
        else nor = forceFalse(nor);
        if (nor) {
          let mtext = el('mtext', {}, out);
          mtext.textContent = nbsp(select('.//m:t', src)
                                      .map(mt => mt.textContent)
                                      .join(''))
          ;
        }
        else {
          select('.//m:t', src)
            .forEach(mt => {
              parseMT(src, out, {
                toParse:  select('./text()', mt).map(t => t.toString()).join(''),
                scr:      selectAttr('../m:rPr[last()]/m:scr', 'm:val', mt),
                sty:      selectAttr('../m:rPr[last()]/m:sty', 'm:val', mt),
                nor:      false,
              });
            })
          ;
        }
      }
    )
    .match(
      m.el('m:limLow'),
      (src, out, w) => {
        let outer = el('munder', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:lim[1]', src));
      }
    )
    .match(
      m.el('m:limUpp'),
      (src, out, w) => {
        let outer = el('mover', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:lim[1]', src));
      }
    )
    .match(
      m.el('m:sSub'),
      (src, out, w) => {
        let outer = el('msub', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:sub[1]', src));
      }
    )
    .match(
      m.el('m:sSup'),
      (src, out, w) => {
        let outer = el('msup', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:sup[1]', src));
      }
    )
    .match(
      m.el('m:sSubSup'),
      (src, out, w) => {
        let outer = el('msubsup', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
          , row3 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:sub[1]', src));
        w.walk(row3, select('m:sup[1]', src));
      }
    )

    .run(omml)
  ;
}

function fracProp (type) {
  console.log(`fracProp(${type})`);
  if (type === 'skw' || type === 'lin') return { bevelled: 'true' };
  if (type === 'nobar') return { linethickness: '0pt' };
  return {};
  // TODO: the original XSLT had traces of trying to set `numalign` on both numerator and
  // denominator, but the variables were never properly defined and could absolutely not match
}

function nbsp (str) {
  if (!str) return;
  return str.replace(/\s/g, '\u00a0');
}

function tf (str) {
  if (str == null) return;
  str = str.toLowerCase();
  if (str === 'on' || str === '1' || str === 'true') return true;
  if (str === 'off' || str === '0' || str === 'false') return false;
}

function forceFalse (str) {
  let res = tf(str);
  if (res === false) return false;
  return true;
}

// function forceTrue (str) {
//   return tf(str) || false;
// }

function parseMT (ctx, out, { toParse = '', scr, sty, nor }) {
  if (!toParse.length) return;
  let firstOper = rxIndexOf(toParse, oprx)
    , firstNum = rxIndexOf(toParse, /\d/)
    , startsWithOper = (firstOper === 1)
    , startsWithNum = (firstNum === 1)
  ;
  if (!startsWithOper && !startsWithNum) {
    let charToPrint;
    if (select('ancestor::m:fName', ctx)[0]) {
      if (!firstOper && !firstNum) charToPrint = toParse.length;
      else charToPrint = Math.min(firstOper || Number.MAX_VALUE, firstNum || Number.MAX_VALUE);
    }
    else charToPrint = 1;
    let mi = el('mi', tokenAttributes({ scr, sty, nor, charToPrint, tokenType: 'mi' }), out);
    mi.textContent = nbsp(toParse.substr(0, charToPrint));
    parseMT(ctx, out, { toParse: toParse.substr(charToPrint), scr, sty, nor });
  }
  else if (startsWithOper) {
    let mo = el('mo', tokenAttributes({ nor, tokenType: 'mo' }), out);
    mo.textContent = toParse.substr(0, 1);
    parseMT(ctx, out, { toParse: toParse.substr(1), scr, sty, nor });
  }
  else {
    let num = numStart(toParse)
      , mn = el('mn', tokenAttributes({ scr, sty: 'p', nor, tokenType: 'mn' }), out)
    ;
    mn.textContent = num;
    parseMT(ctx, out, { toParse: toParse.substr(num.length), scr, sty, nor });
  }
}

function rxIndexOf (str, rx) {
  let re = rx.exec(str);
  if (!re) return 0;
  return re.index + 1;
}

function tokenAttributes ({ scr, sty, nor, charToPrint = 0, tokenType }) {
  let attr = {};
  if (nor) attr.mathvariant = 'normal';
  else {
    let mathvariant
      , fontweight = (sty === 'b' || sty === 'bi') ? 'bold' : 'normal'
      , fontstyle = (sty === 'b' || sty === 'p') ? 'normal' : 'italic'
    ;
    if (tokenType !== 'mn') {
      if (scr === 'monospace') mathvariant = 'monospace';
      else if (scr === 'sans-serif' && sty === 'i') mathvariant = 'sans-serif-italic';
      else if (scr === 'sans-serif' && sty === 'b') mathvariant = 'bold-sans-serif';
      else if (scr === 'sans-serif' && sty === 'bi') mathvariant = 'sans-serif-bold-italic';
      else if (scr === 'sans-serif') mathvariant = 'sans-serif';
      else if (scr === 'fraktur' && (sty === 'b' || sty === 'i')) mathvariant = 'bold-fraktur';
      else if (scr === 'fraktur') mathvariant = 'fraktur';
      else if (scr === 'double-struck') mathvariant = 'double-struck';
      else if (scr === 'script' && (sty === 'b' || sty === 'i')) mathvariant = 'bold-script';
      else if (scr === 'script') mathvariant = 'script';
      else if (scr === 'roman' || !scr) {
        if (sty === 'b') mathvariant = 'bold';
        else if (sty === 'i') mathvariant = 'italic';
        else if (sty === 'p') mathvariant = 'normal';
        else if (sty === 'bi') mathvariant = 'bold-italic';
      }
    }
    if (tokenType === 'mo' && mathvariant !== 'normal') return attr;
    if (tokenType === 'mi' && charToPrint === 1 && (mathvariant === 'italic' || !mathvariant)) {
      return attr;
    }
    if (tokenType === 'mi' && charToPrint > 1 && (mathvariant === 'italic' || !mathvariant)) {
      attr.mathvariant = 'italic';
    }
    else if (mathvariant && mathvariant !== 'italic') {
      attr.mathvariant = mathvariant;
    }
    else {
      if (fontstyle === 'italic' && !(tokenType === 'mi' && charToPrint === 1)) {
        attr.fontstyle = 'italic';
      }
      if (fontweight === 'bold') attr.fontweight = 'bold';
    }
  }
  return attr;
}

function numStart (str) {
  if (!str) return '';
  let ret = '';
  str.replace(/^(\d+)/, (_, m) => {
    ret = m;
  });
  return ret;
}
<commit_msg>mmultiscripts<commit_after>
import Marcheur from 'marcheur';
import nodal from 'marcheur/lib/nodal';
import qname from 'marcheur/lib/qname';
import Matcher from 'marcheur/lib/matcher';
import dom from 'get-dom';
import xpath from 'xpath';
import { oprx } from './operators';

let MATH_NS = 'http://www.w3.org/1998/Math/MathML'
  , nsMap = {
      m:  'http://schemas.openxmlformats.org/officeDocument/2006/math',
    }
  , select = xpath.useNamespaces(nsMap)
  , selectAttr = (path, attr, ctx) => {
      let el = select(path, ctx)[0];
      if (!el) return '';
      let atn = qname(attr, nsMap);
      if (atn.ns) return el.getAttributeNS(atn.ns, atn.ln);
      return el.getAttribute(atn.qn);
    }
  , el
;

export default function omml2mathml (omml) {
  let m = new Matcher(nsMap)
    , walker = new Marcheur()
  ;
  return walker
    // #document
    .match(m.document(),
      (src, out, w) => {
        let doc = dom.implementation().createHTMLDocument('')
          , nod = nodal(doc, {}, nsMap)
          , math = doc.createElementNS(MATH_NS, 'math')
        ;
        math.setAttribute('display', 'inline');
        el = nod.el;
        w.result(math);
        w.walk(math);
      }
    )
    .match(
      m.el('m:oMathPara'),
      (src, out, w) => {
        w.res.setAttribute('display', 'block');
        w.walk(out);
      }
    )
    .match(
      m.el('m:oMath'),
      (src, out, w) => w.walk(out)
    )
    .match(
      m.el('m:f'),
      (src, out, w) => {
        let type = (selectAttr('./m:fPr[last()]/m:type', 'm:val', src) || '').toLowerCase()
          , outer = (type === 'lin')
                      ? el('mrow', {}, out)
                      : el('mfrac', fracProp(type), out)
        ;
        let numRow = el('mrow', {}, outer);
        w.walk(numRow, select('m:num[1]', src));
        if (type === 'lin') {
          let mo = el('mo', {}, outer);
          mo.textContent = '/';
        }
        let denRow = el('mrow', {}, outer);
        w.walk(denRow, select('m:den[1]', src));
      }
    )
    .match(
      m.el('m:r'),
      (src, out) => {
        let nor = selectAttr('m:rPr[last()]/m:nor', 'm:val', src);
        if (!nor) nor = false;
        else nor = forceFalse(nor);
        if (nor) {
          let mtext = el('mtext', {}, out);
          mtext.textContent = nbsp(select('.//m:t', src)
                                      .map(mt => mt.textContent)
                                      .join(''))
          ;
        }
        else {
          select('.//m:t', src)
            .forEach(mt => {
              parseMT(src, out, {
                toParse:  select('./text()', mt).map(t => t.toString()).join(''),
                scr:      selectAttr('../m:rPr[last()]/m:scr', 'm:val', mt),
                sty:      selectAttr('../m:rPr[last()]/m:sty', 'm:val', mt),
                nor:      false,
              });
            })
          ;
        }
      }
    )
    .match(
      m.el('m:limLow'),
      (src, out, w) => {
        let outer = el('munder', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:lim[1]', src));
      }
    )
    .match(
      m.el('m:limUpp'),
      (src, out, w) => {
        let outer = el('mover', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:lim[1]', src));
      }
    )
    .match(
      m.el('m:sSub'),
      (src, out, w) => {
        let outer = el('msub', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:sub[1]', src));
      }
    )
    .match(
      m.el('m:sSup'),
      (src, out, w) => {
        let outer = el('msup', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:sup[1]', src));
      }
    )
    .match(
      m.el('m:sSubSup'),
      (src, out, w) => {
        let outer = el('msubsup', {}, out)
          , row1 = el('mrow', {}, outer)
          , row2 = el('mrow', {}, outer)
          , row3 = el('mrow', {}, outer)
        ;
        w.walk(row1, select('m:e[1]', src));
        w.walk(row2, select('m:sub[1]', src));
        w.walk(row3, select('m:sup[1]', src));
      }
    )
    .match(
      m.el('m:sPre'),
      (src, out, w) => {
        let outer = el('mmultiscripts', {}, out)
          , row = el('mrow', {}, outer)
        ;
        w.walk(row, select('m:e[1]', src));
        el('mprescripts', {}, outer);
        outputScript(w, outer, select('m:sub[1]', src));
        outputScript(w, outer, select('m:sup[1]', src));
      }
    )
    .match(
      m.el('m:m'),
      (src, out, w) => {
        let mcjc = selectAttr('m:mPr[last()]/m:mcs/m:mc/m:mcPr[last()]/m:mcJc/', 'm:val', src)
          , outer = el('mtable', mcjc ? { columnalign: mcjc } : {}, out)
        ;
        select('./m:mr', src)
          .forEach(mr => {
            let mtr = el('mtr', {}, outer);
            select('./m:me', mr)
              .forEach(me => {
                let mtd = el('mtd', {}, mtr);
                w.walk(mtd, me);
              })
            ;
          })
        ;
      }
    )

    .run(omml)
  ;
}

function fracProp (type) {
  console.log(`fracProp(${type})`);
  if (type === 'skw' || type === 'lin') return { bevelled: 'true' };
  if (type === 'nobar') return { linethickness: '0pt' };
  return {};
  // TODO: the original XSLT had traces of trying to set `numalign` on both numerator and
  // denominator, but the variables were never properly defined and could absolutely not match
}

function nbsp (str) {
  if (!str) return;
  return str.replace(/\s/g, '\u00a0');
}

function tf (str) {
  if (str == null) return;
  str = str.toLowerCase();
  if (str === 'on' || str === '1' || str === 'true') return true;
  if (str === 'off' || str === '0' || str === 'false') return false;
}

function forceFalse (str) {
  let res = tf(str);
  if (res === false) return false;
  return true;
}

// function forceTrue (str) {
//   return tf(str) || false;
// }

function parseMT (ctx, out, { toParse = '', scr, sty, nor }) {
  if (!toParse.length) return;
  let firstOper = rxIndexOf(toParse, oprx)
    , firstNum = rxIndexOf(toParse, /\d/)
    , startsWithOper = (firstOper === 1)
    , startsWithNum = (firstNum === 1)
  ;
  if (!startsWithOper && !startsWithNum) {
    let charToPrint;
    if (select('ancestor::m:fName', ctx)[0]) {
      if (!firstOper && !firstNum) charToPrint = toParse.length;
      else charToPrint = Math.min(firstOper || Number.MAX_VALUE, firstNum || Number.MAX_VALUE);
    }
    else charToPrint = 1;
    let mi = el('mi', tokenAttributes({ scr, sty, nor, charToPrint, tokenType: 'mi' }), out);
    mi.textContent = nbsp(toParse.substr(0, charToPrint));
    parseMT(ctx, out, { toParse: toParse.substr(charToPrint), scr, sty, nor });
  }
  else if (startsWithOper) {
    let mo = el('mo', tokenAttributes({ nor, tokenType: 'mo' }), out);
    mo.textContent = toParse.substr(0, 1);
    parseMT(ctx, out, { toParse: toParse.substr(1), scr, sty, nor });
  }
  else {
    let num = numStart(toParse)
      , mn = el('mn', tokenAttributes({ scr, sty: 'p', nor, tokenType: 'mn' }), out)
    ;
    mn.textContent = num;
    parseMT(ctx, out, { toParse: toParse.substr(num.length), scr, sty, nor });
  }
}

function rxIndexOf (str, rx) {
  let re = rx.exec(str);
  if (!re) return 0;
  return re.index + 1;
}

function tokenAttributes ({ scr, sty, nor, charToPrint = 0, tokenType }) {
  let attr = {};
  if (nor) attr.mathvariant = 'normal';
  else {
    let mathvariant
      , fontweight = (sty === 'b' || sty === 'bi') ? 'bold' : 'normal'
      , fontstyle = (sty === 'b' || sty === 'p') ? 'normal' : 'italic'
    ;
    if (tokenType !== 'mn') {
      if (scr === 'monospace') mathvariant = 'monospace';
      else if (scr === 'sans-serif' && sty === 'i') mathvariant = 'sans-serif-italic';
      else if (scr === 'sans-serif' && sty === 'b') mathvariant = 'bold-sans-serif';
      else if (scr === 'sans-serif' && sty === 'bi') mathvariant = 'sans-serif-bold-italic';
      else if (scr === 'sans-serif') mathvariant = 'sans-serif';
      else if (scr === 'fraktur' && (sty === 'b' || sty === 'i')) mathvariant = 'bold-fraktur';
      else if (scr === 'fraktur') mathvariant = 'fraktur';
      else if (scr === 'double-struck') mathvariant = 'double-struck';
      else if (scr === 'script' && (sty === 'b' || sty === 'i')) mathvariant = 'bold-script';
      else if (scr === 'script') mathvariant = 'script';
      else if (scr === 'roman' || !scr) {
        if (sty === 'b') mathvariant = 'bold';
        else if (sty === 'i') mathvariant = 'italic';
        else if (sty === 'p') mathvariant = 'normal';
        else if (sty === 'bi') mathvariant = 'bold-italic';
      }
    }
    if (tokenType === 'mo' && mathvariant !== 'normal') return attr;
    if (tokenType === 'mi' && charToPrint === 1 && (mathvariant === 'italic' || !mathvariant)) {
      return attr;
    }
    if (tokenType === 'mi' && charToPrint > 1 && (mathvariant === 'italic' || !mathvariant)) {
      attr.mathvariant = 'italic';
    }
    else if (mathvariant && mathvariant !== 'italic') {
      attr.mathvariant = mathvariant;
    }
    else {
      if (fontstyle === 'italic' && !(tokenType === 'mi' && charToPrint === 1)) {
        attr.fontstyle = 'italic';
      }
      if (fontweight === 'bold') attr.fontweight = 'bold';
    }
  }
  return attr;
}

function numStart (str) {
  if (!str) return '';
  let ret = '';
  str.replace(/^(\d+)/, (_, m) => {
    ret = m;
  });
  return ret;
}

function outputScript (w, out, cur) {
  if (cur && cur.length) {
    let row = el('mrow', {}, out);
    w.walk(row, cur);
  }
  else el('none', {}, out);
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import documents from '../../data/documents.json';

export default class Documents extends Component {
    render(){
        return (

Documents/Claims

                            Find all documents/claims for transactions below.

                        <button type="submit" className="btn btn-wide btn-o btn-primary">
                            Sign
                        </button>

                                    Select
                                    Document
                                    Created
                                    From
                                    Signed
                                    Approved

                                    <input type="checkbox"/>

                                            Blanket Authorization Form

                                    04-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Closing Package - Promissory Note

                                    01-01-2017
                                    CCU - 2

                                    <input type="checkbox"/>

                                            Closing Package - Confirmation of Deposits, Employment, Credit History

                                    01-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Closing Package - Compliance Agreement

                                    01-01-2016
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Initial Disclosure

                                    03-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Contract 4

                                    04-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Disclosure 1

                                    04-15-2017
                                    Core Credit Union

                            <button type="submit" className="btn btn-wide btn-o btn-primary">
                                Sign
                            </button>

                { this.renderDocuments() }

        );
    }

    renderDocument (document) {
        return (

{document.title}

        )
    }

    renderDocuments () {
        return (

                { documents.payload.map((document) => this.renderDocument(document)) }

        )
    }

}
<commit_msg>Add red `x` icon and styling<commit_after>import React, { Component } from 'react';

import documents from '../../data/documents.json';

export default class Documents extends Component {
    render(){
        return (

Documents/Claims

                            Find all documents/claims for transactions below.

                        <button type="submit" className="btn btn-wide btn-o btn-primary">
                            Sign
                        </button>

                                    Select
                                    Document
                                    Created
                                    From
                                    Signed
                                    Approved

                                    <input type="checkbox"/>

                                            Blanket Authorization Form

                                    04-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Closing Package - Promissory Note

                                    01-01-2017
                                    CCU - 2

                                    <input type="checkbox"/>

                                            Closing Package - Confirmation of Deposits, Employment, Credit History

                                    01-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Closing Package - Compliance Agreement

                                    01-01-2016
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Initial Disclosure

                                    03-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Contract 4

                                    04-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Disclosure 1

                                    04-15-2017
                                    Core Credit Union

                            <button type="submit" className="btn btn-wide btn-o btn-primary">
                                Sign
                            </button>

                { this.renderDocuments() }

        );
    }

    renderDocument (document) {
        return (

{document.title}

        )
    }

    renderDocuments () {
        return (

                { documents.payload.map((document) => this.renderDocument(document)) }

        )
    }

}
<|endoftext|>


<commit_before>import moment from 'moment';
import Server from '../services/Server'
import React, { Component, Text, View, ProgressBarAndroid, NativeModules, StyleSheet, ToastAndroid } from 'react-native';
import MK, { MKButton, MKTextField } from 'react-native-material-kit';
import rnGeolocation from 'rn-geolocation';

export default class Entry extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      email: '',
      duration: '',
      date: moment().format('YYYY-MM-DD'),
      description: '',
      location: {
        longitude: null,
        latitude: null
      }
    };
    this.server = new Server('http://staging-move1t.herokuapp.com');
  }

  sendData() {
    let data = {
      email: this.state.email,
      entry: {
        duration: this.state.duration,
        date: this.state.date,
        description: this.state.description
      },
      location: {
        longitude: this.state.location.longitude,
        latitude: this.state.location.latitude
      }
    };
    console.log(data);
    this.server.post('/entries.json', data)
      .then(() => {
         this.setState({ isLoading: false });
      })
      .catch((err) => {
        this.setState({ isLoading: false });
        ToastAndroid.show('Sorry, we couldn\'t connect to the server', ToastAndroid.SHORT, 2000);
      });
      }
    );
  }

  componentDidMount() {
    rnGeolocation.getCurrentPosition((location) => {
        this.setState({ location: { longitude: location.coords.longitude, latitude: location.coords.latitude }});
    },() => {}, { timeout: 5000, enableHighAccuracy: true, maximumAge: 10000 });
  }

  onSave() {
    this.setState({ isLoading: true });
    this.sendData();
  }

  handleDateClick() {
    var self = this;
    NativeModules.DateAndroid.showDatepicker(function() {}, function(year,month,day) {
      month += 1;
      let newdate = year + '-' + month + '-' +day;
      self.setState({date: newdate});
    });
  }

  render() {
      if(this.state.isLoading) {
        return (
          <View style={styles.progressBar}>
            <ProgressBarAndroid  styleAttr="Inverse"/>
          </View>
        );
      } else {
        return (
          <View>
            <Text onPress={(event) => this.handleDateClick()}>
              Date: {this.state.date}
            </Text>
            <MKTextField
              floatingLabelEnabled={true}
              floatingLabelFont={{fontSize: 15, fontWeight:'100'}}
              keyboardType='email-address'
              onChangeText={(email) => this.setState({email})}
              placeholder='Email'
              value={this.state.email}
              style={styles.textfieldWithFloatingLabel}
            />
            <MKTextField
              floatingLabelEnabled={true}
              floatingLabelFont={{fontSize: 15, fontWeight:'100'}}
              keyboardType='numeric'
              onChangeText={(duration) => this.setState({duration})}
              placeholder='Duration of workout in minutes:'
              value={this.state.duration}
              style={styles.textfieldWithFloatingLabel}
            />
            <MKTextField
              floatingLabelEnabled={true}
              floatingLabelFont={{fontSize: 10, fontWeight:'100'}}
              keyboardType='default'
              onChangeText={(description) => this.setState({description})}
              placeholder='Brief description:'
              value={this.state.description}
              style={styles.textfieldWithFloatingLabel}
            />

            <MKButton
              backgroundColor={'#43ca01'}
              style={{height: 38, padding: 10, margin: 10}}
              onPress={() => this.onSave()}
            >
              <Text pointerEvents="none"
                style={{color: 'white', fontWeight: 'bold', textAlign: 'center'}}>
                SAVE
              </Text>
            </MKButton>

          </View>
        );
      }
    }
  }

let styles = StyleSheet.create({
  textfieldWithFloatingLabel: {
    height: 50,
    marginTop: 10,
  },
  progressBar: {
    flex: 1,
    justifyContent: 'space-around',
    alignItems: 'center'
  },
});
<commit_msg>Add unable to connect msg in entry page<commit_after>import moment from 'moment';
import Server from '../services/Server'
import React, { Component, Text, View, ProgressBarAndroid, NativeModules, StyleSheet, ToastAndroid } from 'react-native';
import MK, { MKButton, MKTextField } from 'react-native-material-kit';
import rnGeolocation from 'rn-geolocation';

export default class Entry extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      email: '',
      duration: '',
      date: moment().format('YYYY-MM-DD'),
      description: '',
      location: {
        longitude: null,
        latitude: null
      }
    };
    this.server = new Server('http://staging-move1t.herokuapp.com');
  }

  sendData() {
    let data = {
      email: this.state.email,
      entry: {
        duration: this.state.duration,
        date: this.state.date,
        description: this.state.description
      },
      location: {
        longitude: this.state.location.longitude,
        latitude: this.state.location.latitude
      }
    };
    this.server.post('/entries.json', data)
      .then(() => {
         this.setState({ isLoading: false });
      })
      .catch((err) => {
        this.setState({ isLoading: false });
        ToastAndroid.show('Sorry, we couldn\'t connect to the server', ToastAndroid.SHORT, 2000);
      });
  }

  componentDidMount() {
    rnGeolocation.getCurrentPosition((location) => {
        this.setState({ location: { longitude: location.coords.longitude, latitude: location.coords.latitude }});
    },() => {}, { timeout: 5000, enableHighAccuracy: true, maximumAge: 10000 });
  }

  onSave() {
    this.setState({ isLoading: true });
    this.sendData();
  }

  handleDateClick() {
    var self = this;
    NativeModules.DateAndroid.showDatepicker(function() {}, function(year,month,day) {
      month += 1;
      let newdate = year + '-' + month + '-' +day;
      self.setState({date: newdate});
    });
  }

  render() {
      if(this.state.isLoading) {
        return (
          <View style={styles.progressBar}>
            <ProgressBarAndroid  styleAttr="Inverse"/>
          </View>
        );
      } else {
        return (
          <View>
            <Text onPress={(event) => this.handleDateClick()}>
              Date: {this.state.date}
            </Text>
            <MKTextField
              floatingLabelEnabled={true}
              floatingLabelFont={{fontSize: 15, fontWeight:'100'}}
              keyboardType='email-address'
              onChangeText={(email) => this.setState({email})}
              placeholder='Email'
              value={this.state.email}
              style={styles.textfieldWithFloatingLabel}
            />
            <MKTextField
              floatingLabelEnabled={true}
              floatingLabelFont={{fontSize: 15, fontWeight:'100'}}
              keyboardType='numeric'
              onChangeText={(duration) => this.setState({duration})}
              placeholder='Duration of workout in minutes:'
              value={this.state.duration}
              style={styles.textfieldWithFloatingLabel}
            />
            <MKTextField
              floatingLabelEnabled={true}
              floatingLabelFont={{fontSize: 10, fontWeight:'100'}}
              keyboardType='default'
              onChangeText={(description) => this.setState({description})}
              placeholder='Brief description:'
              value={this.state.description}
              style={styles.textfieldWithFloatingLabel}
            />

            <MKButton
              backgroundColor={'#43ca01'}
              style={{height: 38, padding: 10, margin: 10}}
              onPress={() => this.onSave()}
            >
              <Text pointerEvents="none"
                style={{color: 'white', fontWeight: 'bold', textAlign: 'center'}}>
                SAVE
              </Text>
            </MKButton>

          </View>
        );
      }
    }
  }

let styles = StyleSheet.create({
  textfieldWithFloatingLabel: {
    height: 50,
    marginTop: 10,
  },
  progressBar: {
    flex: 1,
    justifyContent: 'space-around',
    alignItems: 'center'
  },
});
<|endoftext|>