<commit_before>const RuleEngine = require('node-rules');
const _ = require('lodash');
const config = require('config');
const logger = require('cli-logger');
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 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 log = logger({ level: logger.INFO });

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;
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, 'trend') &&
      _.has(this, 'market') &&
      _.has(this, 'lastTrade') &&
      this.event === 'crossover' &&
      this.trend === 'DOWN' &&
      this.lastTrade !== 'BUY' &&
      this.market === 'BULL-OR-FLAT');
  },
  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, 'lastTrade') &&
      _.has(this, 'market') &&
      _.has(this, 'lastBuyPrice') &&
      _.has(this, 'currentBidPrice') &&
      _.has(this, 'rangePercentage') &&
      this.event === 'crossover' &&
      this.currentBidPrice > (this.lastBuyPrice + (this.rangePercentage * this.lastBuyPrice)) &&
      this.lastTrade !== 'SELL' &&
      this.market === 'BULL-OR-FLAT');
  },
  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, 'event') &&
      _.has(this, 'lastTrade') &&
      _.has(this, 'market') &&
      this.event === 'crossover' &&
      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 <= movingAverageLong) {
    currentMarket.market = 'BEAR';
  } else {
    currentMarket.market = 'BULL-OR-FLAT';
  }
  log.info(`getMarketTrend, trend : ${currentMarket.trend}, market: ${(currentMarket.market || 'nevermind')}`);

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

  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')),
    getTicker(config.get('bittrexMarket')),
  ];

  const [range, ticker] = await Promise.all(tasks);

  if (lastBuyPrice > 0) {
    fact.lastBuyPrice = lastBuyPrice;
    fact.rangePercentage = range / ticker.Bid;
    fact.currentBidPrice = ticker.Bid;
  }
  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)) {
    lastBuyPrice = price;

    // trade was successful
    lastTradeTime = new Date().getTime();
    lastTrade = action;
    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);
  }

  const tasks = [
    getBalances(),
    getTicker(config.get('bittrexMarket')),
  ];
  const timeSinceLastTrade = new Date().getTime() - lastTradeTime;
  if (timeSinceLastTrade < config.get('strategy.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.warn(`buySecurity, 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('buySecurity, trade: false. Skipping security trades');
    return (false);
  }

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

  const [bittrexBalances, ticker] = await Promise.all(tasks);
  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, 'SELL'); }, config.get('balancePollInterval'));
    return (true);
  }

  log.warn('sellSecurity: 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((bittrexBalances) => {
  const account = new Account();
  if (account.setBittrexBalance(bittrexBalances) > 1) {
    // Some crypto currency should have been sold to have this balance
    lastTrade = 'SELL';
  } else {
    lastTrade = 'BUY';
  }

  // TODO : Cancel all open orders when the script starts
});
<commit_msg>bug fix<commit_after>const RuleEngine = require('node-rules');
const _ = require('lodash');
const config = require('config');
const logger = require('cli-logger');
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 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 log = logger({ level: logger.INFO });

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;
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, 'trend') &&
      _.has(this, 'market') &&
      _.has(this, 'lastTrade') &&
      this.event === 'crossover' &&
      this.trend === 'DOWN' &&
      this.lastTrade !== 'BUY' &&
      this.market === 'BULL-OR-FLAT');
  },
  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, 'lastTrade') &&
      _.has(this, 'market') &&
      _.has(this, 'lastBuyPrice') &&
      _.has(this, 'currentBidPrice') &&
      _.has(this, 'rangePercentage') &&
      this.event === 'crossover' &&
      this.currentBidPrice > (this.lastBuyPrice + (this.rangePercentage * this.lastBuyPrice)) &&
      this.lastTrade !== 'SELL' &&
      this.market === 'BULL-OR-FLAT');
  },
  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, 'event') &&
      _.has(this, 'lastTrade') &&
      _.has(this, 'market') &&
      this.event === 'crossover' &&
      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 <= movingAverageLong) {
    currentMarket.market = 'BEAR';
  } else {
    currentMarket.market = 'BULL-OR-FLAT';
  }
  log.info(`getMarketTrend, trend : ${currentMarket.trend}, market: ${(currentMarket.market || 'nevermind')}`);

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

  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')),
    getTicker(config.get('bittrexMarket')),
  ];

  const [range, ticker] = await Promise.all(tasks);

  if (lastBuyPrice > 0) {
    fact.lastBuyPrice = lastBuyPrice;
    fact.rangePercentage = range / ticker.Bid;
    fact.currentBidPrice = ticker.Bid;
  }
  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)) {
    lastBuyPrice = price;

    // trade was successful
    lastTradeTime = new Date().getTime();
    lastTrade = action;
    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);
  }

  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.warn(`buySecurity, 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('buySecurity, trade: false. Skipping security trades');
    return (false);
  }

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

  const [bittrexBalances, ticker] = await Promise.all(tasks);
  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, 'SELL'); }, config.get('balancePollInterval'));
    return (true);
  }

  log.warn('sellSecurity: 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((bittrexBalances) => {
  const account = new Account();
  if (account.setBittrexBalance(bittrexBalances) > 1) {
    // Some crypto currency should have been sold to have this balance
    lastTrade = 'SELL';
  } else {
    lastTrade = 'BUY';
  }

  // TODO : Cancel all open orders when the script starts
});
<|endoftext|>


<commit_before>module.exports = capitalizeString;

function capitalizeString(string) {
    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
<commit_msg>updates<commit_after>module.exports = capitalizeString;

function capitalizeString(string) {
    return string.charAt(0).toUpperCase() + string.slice(1);
}
<|endoftext|>


<commit_before>var exec = require("cordova/exec");
var deviceVendor =
  typeof window.clientInformation != "undefined" &&
  typeof window.clientInformation.vendor != "undefined"
    ? window.clientInformation.vendor
    : "unknownVendor";

// SDK Class
var API_CLASS = "BranchSDK";

const standardEvent = {
  STANDARD_EVENT_ADD_TO_CART: "ADD_TO_CART",
  STANDARD_EVENT_ADD_TO_WISHLIST: "ADD_TO_WISHLIST",
  STANDARD_EVENT_VIEW_CART: "VIEW_CART",
  STANDARD_EVENT_INITIATE_PURCHASE: "INITIATE_PURCHASE",
  STANDARD_EVENT_ADD_PAYMENT_INFO: "ADD_PAYMENT_INFO",
  STANDARD_EVENT_PURCHASE: "PURCHASE",
  STANDARD_EVENT_SPEND_CREDITS: "SPEND_CREDITS",
  STANDARD_EVENT_SEARCH: "SEARCH",
  STANDARD_EVENT_VIEW_ITEM: "VIEW_ITEM",
  STANDARD_EVENT_VIEW_ITEMS: "VIEW_ITEMS",
  STANDARD_EVENT_RATE: "RATE",
  STANDARD_EVENT_SHARE: "SHARE",
  STANDARD_EVENT_COMPLETE_REGISTRATION: "COMPLETE_REGISTRATION",
  STANDARD_EVENT_COMPLETE_TUTORIAL: "COMPLETE_TUTORIAL",
  STANDARD_EVENT_ACHIEVE_LEVEL: "ACHIEVE_LEVEL",
  STANDARD_EVENT_UNLOCK_ACHIEVEMENT: "UNLOCK_ACHIEVEMENT"
}

// Branch prototype
var Branch = function Branch() {
  this.debugMode = false;
  this.trackingDisabled = false;
};

// JavsSript to SDK wrappers
function execute(method, params) {
  var output = !params ? [] : params;

  if (method == "getStandardEvents") {
    return new Promise(function promise(resolve, reject) {
      resolve(standardEvent);
    });
  }

  return new Promise(function promise(resolve, reject) {
    exec(
      function success(res) {
        resolve(res);
      },
      function failure(err) {
        reject(err);
      },
      API_CLASS,
      method,
      output
    );
  });
}

function executeCallback(method, callback, params) {
  var output = !params ? [] : params;

  exec(
    callback,
    function failure(err) {
      console.error(err);
    },
    API_CLASS,
    method,
    output
  );
}

function executeReject(message) {
  return new Promise(function promise(resolve, reject) {
    reject(message);
  });
}

// Branch interface
Branch.prototype.disableTracking = function disableTracking(isEnabled) {
  var value = typeof isEnabled === "boolean" ? isEnabled : false;
  this.trackingDisabled = value;
  return execute("disableTracking", [value]);
};

Branch.prototype.initSession = function initSession() {
  return execute("initSession");
};

Branch.prototype.setRequestMetadata = function setRequestMetadata(key, val) {
  if (!key || typeof key !== "string") {
    return executeReject("Please set key");
  }
  if (!val || typeof val !== "string") {
    return executeReject("Please set value");
  }
  return execute("setRequestMetadata", [key, val]);
};

Branch.prototype.setDebug = function setDebug(isEnabled) {
  var value = typeof isEnabled !== "boolean" ? false : isEnabled;
  this.debugMode = value;

  return execute("setDebug", [value]);
};

Branch.prototype.setCookieBasedMatching = function setCookieBasedMatching(
  linkDomain
) {
  return linkDomain && deviceVendor.indexOf("Apple") < 0
    ? execute("setCookieBasedMatching", [linkDomain])
    : null;
};

Branch.prototype.delayInitToCheckForSearchAds = function delayInitToCheckForSearchAds(
  isEnabled
) {
  var value = typeof isEnabled !== "boolean" ? false : isEnabled;

  return execute("delayInitToCheckForSearchAds", [value]);
};

Branch.prototype.getFirstReferringParams = function getFirstReferringParams() {
  return execute("getFirstReferringParams");
};

Branch.prototype.getLatestReferringParams = function getLatestReferringParams() {
  return execute("getLatestReferringParams");
};

Branch.prototype.setIdentity = function setIdentity(identity) {
  if (identity) {
    return execute("setIdentity", [String(identity)]);
  }
  return executeReject("Please set an identity");
};

Branch.prototype.logout = function logout() {
  return execute("logout");
};

//DEPRECATED
Branch.prototype.userCompletedAction = function userCompletedAction(
  action,
  metaData
) {
  var args = [action];
  if (!action) {
    return executeReject("Please set an event name");
  }

  if (metaData) {
    args.push(metaData);
  }

  return execute("userCompletedAction", args);
};

//DEPRECATED
Branch.prototype.sendCommerceEvent = function sendCommerceEvent(
  action,
  metaData
) {
  var args = [action];
  if (!action) {
    return executeReject("Please set a commerce event");
  }

  if (metaData) {
    args.push(metaData);
  }

  return execute("sendCommerceEvent", args);
};

Branch.prototype.getStandardEvents = function getStandardEvents() {
  return execute("getStandardEvents");

};

Branch.prototype.sendBranchEvent = function sendBranchEvent(
  action,
  metaData
) {
  var args = [action];
  if (!action) {
    return executeReject("Please set a standard event");
  }

  if (metaData) {
    args.push(metaData);
  }

  return execute("sendBranchEvent", args);
};

Branch.prototype.createBranchUniversalObject = function createBranchUniversalObject(
  options
) {
  return new Promise(function promise(resolve, reject) {
    execute("createBranchUniversalObject", [options]).then(
      function success(res) {
        var obj = {
          message: res.message,
          instanceId: res.branchUniversalObjectId
        };

        obj.registerView = function registerView() {
          return execute("registerView", [obj.instanceId]);
        };

        obj.generateShortUrl = function generateShortUrl(
          analytics,
          properties
        ) {
          return execute("generateShortUrl", [
            obj.instanceId,
            analytics,
            properties
          ]);
        };

        obj.showShareSheet = function showShareSheet(
          analytics,
          properties,
          shareText
        ) {
          var message = !shareText ? "This stuff is awesome: " : shareText;

          return execute("showShareSheet", [
            obj.instanceId,
            analytics,
            properties,
            message
          ]);
        };

        obj.onShareSheetLaunched = function onShareSheetLaunched(callback) {
          if (deviceVendor.indexOf("Apple") < 0) {
            executeCallback("onShareLinkDialogLaunched", callback, [
              obj.instanceId
            ]);
          }
        };

        obj.onShareSheetDismissed = function onShareSheetDismissed(callback) {
          executeCallback("onShareLinkDialogDismissed", callback, [
            obj.instanceId
          ]);
        };

        obj.onLinkShareResponse = function onLinkShareResponse(callback) {
          executeCallback("onLinkShareResponse", callback, [obj.instanceId]);
        };

        obj.onChannelSelected = function onChannelSelected(callback) {
          if (deviceVendor.indexOf("Apple") < 0) {
            executeCallback("onChannelSelected", callback, [obj.instanceId]);
          }
        };

        obj.listOnSpotlight = function listOnSpotlight() {
          if (!(deviceVendor.indexOf("Apple") < 0)) {
            return execute("listOnSpotlight", [obj.instanceId]);
          }
          return executeReject("iOS Spotlight only");
        };

        resolve(obj);
      },
      function failure(err) {
        reject(err);
      }
    );
  });
};

Branch.prototype.loadRewards = function loadRewards(bucket) {
  var output = !bucket ? "" : bucket;
  return execute("loadRewards", [output]);
};

Branch.prototype.redeemRewards = function redeemRewards(value, bucket) {
  var params = [value];
  if (bucket) {
    params.push(bucket);
  }

  return execute("redeemRewards", params);
};

Branch.prototype.creditHistory = function creditHistory() {
  return execute("getCreditHistory");
};

Branch.prototype.crossPlatformIds = function crossPlatformIds() {
  return execute("crossPlatformIds");
};

Branch.prototype.lastAttributedTouchData = function lastAttributedTouchData() {
  return execute("lastAttributedTouchData");
};

// export Branch object
module.exports = new Branch();
<commit_msg>deprecate delay for apple search ads method<commit_after>var exec = require("cordova/exec");
var deviceVendor =
  typeof window.clientInformation != "undefined" &&
  typeof window.clientInformation.vendor != "undefined"
    ? window.clientInformation.vendor
    : "unknownVendor";

// SDK Class
var API_CLASS = "BranchSDK";

const standardEvent = {
  STANDARD_EVENT_ADD_TO_CART: "ADD_TO_CART",
  STANDARD_EVENT_ADD_TO_WISHLIST: "ADD_TO_WISHLIST",
  STANDARD_EVENT_VIEW_CART: "VIEW_CART",
  STANDARD_EVENT_INITIATE_PURCHASE: "INITIATE_PURCHASE",
  STANDARD_EVENT_ADD_PAYMENT_INFO: "ADD_PAYMENT_INFO",
  STANDARD_EVENT_PURCHASE: "PURCHASE",
  STANDARD_EVENT_SPEND_CREDITS: "SPEND_CREDITS",
  STANDARD_EVENT_SEARCH: "SEARCH",
  STANDARD_EVENT_VIEW_ITEM: "VIEW_ITEM",
  STANDARD_EVENT_VIEW_ITEMS: "VIEW_ITEMS",
  STANDARD_EVENT_RATE: "RATE",
  STANDARD_EVENT_SHARE: "SHARE",
  STANDARD_EVENT_COMPLETE_REGISTRATION: "COMPLETE_REGISTRATION",
  STANDARD_EVENT_COMPLETE_TUTORIAL: "COMPLETE_TUTORIAL",
  STANDARD_EVENT_ACHIEVE_LEVEL: "ACHIEVE_LEVEL",
  STANDARD_EVENT_UNLOCK_ACHIEVEMENT: "UNLOCK_ACHIEVEMENT"
}

// Branch prototype
var Branch = function Branch() {
  this.debugMode = false;
  this.trackingDisabled = false;
};

// JavsSript to SDK wrappers
function execute(method, params) {
  var output = !params ? [] : params;

  if (method == "getStandardEvents") {
    return new Promise(function promise(resolve, reject) {
      resolve(standardEvent);
    });
  }

  return new Promise(function promise(resolve, reject) {
    exec(
      function success(res) {
        resolve(res);
      },
      function failure(err) {
        reject(err);
      },
      API_CLASS,
      method,
      output
    );
  });
}

function executeCallback(method, callback, params) {
  var output = !params ? [] : params;

  exec(
    callback,
    function failure(err) {
      console.error(err);
    },
    API_CLASS,
    method,
    output
  );
}

function executeReject(message) {
  return new Promise(function promise(resolve, reject) {
    reject(message);
  });
}

// Branch interface
Branch.prototype.disableTracking = function disableTracking(isEnabled) {
  var value = typeof isEnabled === "boolean" ? isEnabled : false;
  this.trackingDisabled = value;
  return execute("disableTracking", [value]);
};

Branch.prototype.initSession = function initSession() {
  return execute("initSession");
};

Branch.prototype.setRequestMetadata = function setRequestMetadata(key, val) {
  if (!key || typeof key !== "string") {
    return executeReject("Please set key");
  }
  if (!val || typeof val !== "string") {
    return executeReject("Please set value");
  }
  return execute("setRequestMetadata", [key, val]);
};

Branch.prototype.setDebug = function setDebug(isEnabled) {
  var value = typeof isEnabled !== "boolean" ? false : isEnabled;
  this.debugMode = value;

  return execute("setDebug", [value]);
};

Branch.prototype.setCookieBasedMatching = function setCookieBasedMatching(
  linkDomain
) {
  return linkDomain && deviceVendor.indexOf("Apple") < 0
    ? execute("setCookieBasedMatching", [linkDomain])
    : null;
};

//DEPRECATED
Branch.prototype.delayInitToCheckForSearchAds = function delayInitToCheckForSearchAds(
  isEnabled
) {
  // stub call from known issue calling it from JS
  return new Promise(function promise(resolve, reject) {
    resolve(false);
  });

  // var value = typeof isEnabled !== "boolean" ? false : isEnabled;

  // return execute("delayInitToCheckForSearchAds", [value]);
};

Branch.prototype.getFirstReferringParams = function getFirstReferringParams() {
  return execute("getFirstReferringParams");
};

Branch.prototype.getLatestReferringParams = function getLatestReferringParams() {
  return execute("getLatestReferringParams");
};

Branch.prototype.setIdentity = function setIdentity(identity) {
  if (identity) {
    return execute("setIdentity", [String(identity)]);
  }
  return executeReject("Please set an identity");
};

Branch.prototype.logout = function logout() {
  return execute("logout");
};

//DEPRECATED
Branch.prototype.userCompletedAction = function userCompletedAction(
  action,
  metaData
) {
  var args = [action];
  if (!action) {
    return executeReject("Please set an event name");
  }

  if (metaData) {
    args.push(metaData);
  }

  return execute("userCompletedAction", args);
};

//DEPRECATED
Branch.prototype.sendCommerceEvent = function sendCommerceEvent(
  action,
  metaData
) {
  var args = [action];
  if (!action) {
    return executeReject("Please set a commerce event");
  }

  if (metaData) {
    args.push(metaData);
  }

  return execute("sendCommerceEvent", args);
};

Branch.prototype.getStandardEvents = function getStandardEvents() {
  return execute("getStandardEvents");

};

Branch.prototype.sendBranchEvent = function sendBranchEvent(
  action,
  metaData
) {
  var args = [action];
  if (!action) {
    return executeReject("Please set a standard event");
  }

  if (metaData) {
    args.push(metaData);
  }

  return execute("sendBranchEvent", args);
};

Branch.prototype.createBranchUniversalObject = function createBranchUniversalObject(
  options
) {
  return new Promise(function promise(resolve, reject) {
    execute("createBranchUniversalObject", [options]).then(
      function success(res) {
        var obj = {
          message: res.message,
          instanceId: res.branchUniversalObjectId
        };

        obj.registerView = function registerView() {
          return execute("registerView", [obj.instanceId]);
        };

        obj.generateShortUrl = function generateShortUrl(
          analytics,
          properties
        ) {
          return execute("generateShortUrl", [
            obj.instanceId,
            analytics,
            properties
          ]);
        };

        obj.showShareSheet = function showShareSheet(
          analytics,
          properties,
          shareText
        ) {
          var message = !shareText ? "This stuff is awesome: " : shareText;

          return execute("showShareSheet", [
            obj.instanceId,
            analytics,
            properties,
            message
          ]);
        };

        obj.onShareSheetLaunched = function onShareSheetLaunched(callback) {
          if (deviceVendor.indexOf("Apple") < 0) {
            executeCallback("onShareLinkDialogLaunched", callback, [
              obj.instanceId
            ]);
          }
        };

        obj.onShareSheetDismissed = function onShareSheetDismissed(callback) {
          executeCallback("onShareLinkDialogDismissed", callback, [
            obj.instanceId
          ]);
        };

        obj.onLinkShareResponse = function onLinkShareResponse(callback) {
          executeCallback("onLinkShareResponse", callback, [obj.instanceId]);
        };

        obj.onChannelSelected = function onChannelSelected(callback) {
          if (deviceVendor.indexOf("Apple") < 0) {
            executeCallback("onChannelSelected", callback, [obj.instanceId]);
          }
        };

        obj.listOnSpotlight = function listOnSpotlight() {
          if (!(deviceVendor.indexOf("Apple") < 0)) {
            return execute("listOnSpotlight", [obj.instanceId]);
          }
          return executeReject("iOS Spotlight only");
        };

        resolve(obj);
      },
      function failure(err) {
        reject(err);
      }
    );
  });
};

Branch.prototype.loadRewards = function loadRewards(bucket) {
  var output = !bucket ? "" : bucket;
  return execute("loadRewards", [output]);
};

Branch.prototype.redeemRewards = function redeemRewards(value, bucket) {
  var params = [value];
  if (bucket) {
    params.push(bucket);
  }

  return execute("redeemRewards", params);
};

Branch.prototype.creditHistory = function creditHistory() {
  return execute("getCreditHistory");
};

Branch.prototype.crossPlatformIds = function crossPlatformIds() {
  return execute("crossPlatformIds");
};

Branch.prototype.lastAttributedTouchData = function lastAttributedTouchData() {
  return execute("lastAttributedTouchData");
};

// export Branch object
module.exports = new Branch();
<|endoftext|>


<commit_before>import $$observable from 'symbol-observable';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';

function isEvent(e) {
  return e && e.preventDefault && e.stopPropagation;
}

const hasRaf = typeof window !== 'undefined' && window.requestAnimationFrame;

export function run(program, render, opt = {}) {
  const { init, view, update, subscriptions } = program.main;
  const { onUpdate } = opt;
  const msg$ = new Subject();
  let initModel = init, sideEffect;

  // Use requestAnimationFrame for better render performance
  const rafRender = hasRaf
    ? vdom => requestAnimationFrame(() => render(vdom))
    : render;

  const platform = {
    cmd: msg$.next.bind(msg$),
    subscribe: msg$.subscribe.bind(msg$),
    [$$observable]() {
      return platform;
    }
  };

  // alias
  platform.sub = platform.subscribe;

  if (typeof initModel === 'undefined') {
    throw new Error('Initial model is undefined. Pass null to explicitly set `no value`.');
  }

  if (Array.isArray(initModel) && typeof initModel[1] === 'function') {
    [initModel, sideEffect] = initModel;
  }

  const model$ = msg$
    .startWith(initModel)
    .scan((model, msg) => {
      let newModel = update(msg, model);

      if (typeof newModel === 'undefined') {
        throw new Error(`Unhandled msg ${msg.constructor.name} ${JSON.stringify(msg)}`);
      }

      if (Array.isArray(newModel) && typeof newModel[1] === 'function') {
        [newModel, sideEffect] = newModel;
      }

      onUpdate && onUpdate(msg, model, newModel);
      return newModel;
    })
    ._do(() => {
      sideEffect && sideEffect(platform);
      sideEffect = null;
    });

  let view$ = view(dispatch, model$);
  if (typeof view$ === 'function') {
    view$ = model$.map(view$);
  }

  view$.subscribe(rafRender);

  subscriptions && subscriptions(platform, initModel);

  function dispatch(msg, ...args) {
    if (typeof msg === 'function') {
      return function eventHandler(e) {
        if (isEvent(e)) {
          const { type, target } = e;
          if (type === 'change' && target.tagName === 'INPUT' && (target.type === 'radio' || target.type === 'checkbox')) {
            msg$.next(new msg(...args, target.checked));
          } else {
            msg$.next(new msg(...args, target.value));
          }
        } else {
          msg$.next(new msg(...args));
        }
      }
    }

    msg$.next(msg);
  }

  return {
    platform,
    terminate: msg$.complete.bind(msg$)
  };
}

export function caseOf(msg, ...params) {
  for (let i = 0; i < params.length ; i += 2) {
    const Msg = params[i];
    const update = params[i + 1];
    if (msg.constructor === Msg) {
      return update(msg);
    }
  }
}<commit_msg>Remove platform<commit_after>import $$observable from 'symbol-observable';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';

function isEvent(e) {
  return e && e.preventDefault && e.stopPropagation;
}

export function run(program, options = {}) {
  const { init, view, update } = program.main;
  const { onMsg, onModel, onView } = options;
  const msg$ = new Subject();
  let { subscriptions } = program.main;
  let initModel = init;
  let sideEffect;

  if (typeof initModel === 'undefined') {
    throw new Error('Initial model is undefined. Pass null to explicitly set `no value`.');
  }

  if (Array.isArray(initModel) && typeof initModel[1] === 'function') {
    [initModel, sideEffect] = initModel;
  }

  const model$ = msg$
    .startWith(initModel)
    .scan((model, msg) => {
      if (typeof msg !== 'object') {
        throw new Error(`Expected msg to be an object.`);
      }

      onMsg && onMsg(msg);
      let newModel = update(msg, model);

      if (typeof newModel === 'undefined') {
        throw new Error(`Unhandled msg ${msg.constructor.name} ${JSON.stringify(msg)}`);
      }

      if (Array.isArray(newModel) && typeof newModel[1] === 'function') {
        [newModel, sideEffect] = newModel;
      }

      return newModel;
    })
    ._do(model => {
      onModel && onModel(model);

      if (subscriptions) {
        subscriptions(dispatch, initModel);
        subscriptions = null;
      }

      if (sideEffect) {
        sideEffect(dispatch);
        sideEffect = null;
      }
    });

  let view$ = view(dispatch, model$);
  if (typeof view$ === 'function') {
    view$ = model$.map(view$);
  }

  view$.subscribe(onView);

  function dispatch(msg, ...args) {
    if (typeof msg === 'function') {
      return function eventHandler(e) {
        if (isEvent(e)) {
          const { type, target } = e;
          if (type === 'change' && target.tagName === 'INPUT' && (target.type === 'radio' || target.type === 'checkbox')) {
            msg$.next(new msg(...args, target.checked));
          } else {
            msg$.next(new msg(...args, target.value));
          }
        } else {
          msg$.next(new msg(...arguments));
        }
      }
    }

    msg$.next(msg);
  }

  return {
    dispatch,
    abort: msg$.complete.bind(msg$)
  };
}

export function caseOf(msg, ...params) {
  for (let i = 0; i < params.length ; i += 2) {
    const Msg = params[i];
    const update = params[i + 1];
    if (msg.constructor === Msg) {
      return update(msg);
    }
  }
}

export function assignArgs(instance, args) {
  // https://davidwalsh.name/javascript-arguments
  const argNames = instance
    .constructor
    .toString()
    .match(/function\s.*?\(([^)]*)\)/)[1]
    .split(',')
    .map(arg => arg.replace(/\/\*.*\*\//, '').trim())
    .filter(arg => arg);

  const argMap = {};
  argNames.forEach((n, i) => argMap[n] = args[i]);
  Object.assign(instance, argMap);
}<|endoftext|>


<commit_before>// index.js
//

var module = require('module'),
    path = require('path');
var app = require('./app.js');

/**
 * Runs the application.
 */
function run() {
  var appPath = path.dirname(require.main.filename);
  var appRequire = require.main.require.bind(require.main);

  if (app.initialize(appPath, appRequire)) {
    var server = require('./server.js');

    server.initialize();
    server.run();
  }
}

exports.run = run;
<commit_msg>App startup code factoring<commit_after>// index.js
//

var module = require('module'),
    path = require('path');
var app = require('./app.js');

function initializeApplication() {
  var appPath = path.dirname(require.main.filename);
  var appRequire = require.main.require.bind(require.main);

  return app.initialize(appPath, appRequire));
}

function runWebServer() {
  if (initializeApplication()) {
    var server = require('./server.js');

    server.initialize();
    server.run();
  }
}

function run() {
  // TODO: Support for other modes ... eg. worker and task
  runWebServer();
}

exports.run = run;
<|endoftext|>


<commit_before>// @flow
const { execSync, spawn } = require('child_process')
const path = require('path')
const { RunJSError, logger } = require('./common')

const loggerAlias = logger

type Options = {
  cwd?: string,
  async?: boolean,
  stdio?: string | Array<any>,
  env?: Object,
  timeout?: number
}

function runSync (command: string, options: Options) : ?string {
  try {
    const nextOptions = {
      cwd: options.cwd,
      env: options.env,
      stdio: options.stdio,
      timeout: options.timeout
    }
    const buffer: string | Buffer = execSync(command, nextOptions)
    if (buffer) {
      return buffer.toString()
    }
    return null
  } catch (error) {
    throw new RunJSError(error.message)
  }
}

function runAsync (command: string, options: Options): Promise<?string> {
  return new Promise((resolve, reject) => {
    const nextOptions = {
      cwd: options.cwd,
      env: options.env,
      stdio: options.stdio,
      shell: true
    }
    const asyncProcess = spawn(command, nextOptions)
    let output : ?string = null

    asyncProcess.on('error', (error: Error) => {
      reject(new Error(`Failed to start command: ${command}; ${error.toString()}`))
    })

    asyncProcess.on('close', (exitCode: number) => {
      if (exitCode === 0) {
        resolve(output)
      } else {
        reject(new Error(`Command failed: ${command} with exit code ${exitCode}`))
      }
    })

    if (options.stdio === 'pipe') {
      asyncProcess.stdout.on('data', (buffer: Buffer) => {
        output = buffer.toString()
      })
    }

    if (options.timeout) {
      setTimeout(() => {
        asyncProcess.kill()
        reject(new Error(`Command timeout: ${command}`))
      }, options.timeout)
    }
  })
}

function run (command: string, options: Options = {}, logger = loggerAlias): Promise<?string> | ?string {
  const binPath = path.resolve('./node_modules/.bin')

  // Pick relevant option keys and set default values
  const nextOptions: Options = {
    env: options.env || process.env,
    cwd: options.cwd,
    async: !!options.async,
    stdio: options.stdio || 'inherit',
    timeout: options.timeout
  }

  const env = options.env

  // Include in PATH node_modules bin path
  if (env) {
    env.PATH = [binPath, env.PATH || process.env.PATH].join(path.delimiter)
  }

  logger.title(command)

  // Handle async call
  if (options.async) {
    return runAsync(command, nextOptions)
  }

  // Handle sync call by default
  return runSync(command, nextOptions)
}

/**
 * @deprecated
 */
function option (thisObj: ?Object, name: string): mixed {
  return (thisObj && thisObj.options && thisObj.options[name]) || null
}

function options (thisObj: ?Object): Object {
  return (thisObj && thisObj.options) || {}
}

module.exports = {
  run,
  option,
  options
}
<commit_msg>api: Improve types<commit_after>// @flow
const { execSync, spawn } = require('child_process')
const path = require('path')
const { RunJSError, logger, Logger } = require('./common')

const loggerAlias: Logger = logger

type Options = {
  cwd?: string,
  async?: boolean,
  stdio?: string | Array<any>,
  env?: Object,
  timeout?: number
}

function runSync (command: string, options: Options) : ?string {
  try {
    const nextOptions = {
      cwd: options.cwd,
      env: options.env,
      stdio: options.stdio,
      timeout: options.timeout
    }
    const buffer: string | Buffer = execSync(command, nextOptions)
    if (buffer) {
      return buffer.toString()
    }
    return null
  } catch (error) {
    throw new RunJSError(error.message)
  }
}

function runAsync (command: string, options: Options): Promise<?string> {
  return new Promise((resolve, reject) => {
    const nextOptions = {
      cwd: options.cwd,
      env: options.env,
      stdio: options.stdio,
      shell: true
    }
    const asyncProcess = spawn(command, nextOptions)
    let output : ?string = null

    asyncProcess.on('error', (error: Error) => {
      reject(new Error(`Failed to start command: ${command}; ${error.toString()}`))
    })

    asyncProcess.on('close', (exitCode: number) => {
      if (exitCode === 0) {
        resolve(output)
      } else {
        reject(new Error(`Command failed: ${command} with exit code ${exitCode}`))
      }
    })

    if (options.stdio === 'pipe') {
      asyncProcess.stdout.on('data', (buffer: Buffer) => {
        output = buffer.toString()
      })
    }

    if (options.timeout) {
      setTimeout(() => {
        asyncProcess.kill()
        reject(new Error(`Command timeout: ${command}`))
      }, options.timeout)
    }
  })
}

function run (command: string, options: Options = {}, logger: Logger = loggerAlias): Promise<?string> | ?string {
  const binPath = path.resolve('./node_modules/.bin')

  // Pick relevant option keys and set default values
  const nextOptions: Options = {
    env: options.env || process.env,
    cwd: options.cwd,
    async: !!options.async,
    stdio: options.stdio || 'inherit',
    timeout: options.timeout
  }

  const env = options.env

  // Include in PATH node_modules bin path
  if (env) {
    env.PATH = [binPath, env.PATH || process.env.PATH].join(path.delimiter)
  }

  logger.title(command)

  // Handle async call
  if (options.async) {
    return runAsync(command, nextOptions)
  }

  // Handle sync call by default
  return runSync(command, nextOptions)
}

/**
 * @deprecated
 */
function option (thisObj: ?Object, name: string): mixed {
  return (thisObj && thisObj.options && thisObj.options[name]) || null
}

function options (thisObj: ?Object): Object {
  return (thisObj && thisObj.options) || {}
}

module.exports = {
  run,
  option,
  options
}
<|endoftext|>


<commit_before>import events from 'events';
import fs from 'fs';
import timers from 'timers';
import stream, { Transform } from 'stream';

const noop = () => {};

const streamify = (text) => {
    const s = new stream.Readable();
    s.push(text);
    s.push(null);
    return s;
};

const containsLineEnd = (() => {
    const re = new RegExp(/.*(?:\r\n|\r|\n)/g);

    return (s => !!s.match(re));
})();

// @param {array} arr The array to iterate over.
// @param {object} opts The options object.
// @param {function} iteratee The iteratee invoked per element.
// @param {function} done The done invoked after the loop has finished.
const iterateArray = (arr = [], opts = {}, iteratee = noop, done = noop) => {
    if (typeof opts === 'function') {
        done = iteratee;
        iteratee = opts;
        opts = {};
    }

    opts.batchSize = opts.batchSize || 1;

    const loop = (i = 0) => {
        for (let count = 0; i < arr.length && count < opts.batchSize; ++i, ++count) {
            iteratee(arr[i], i, arr);
        }
        if (i < arr.length) {
            timers.setImmediate(() => loop(i));
            return;
        }
        done();
    };
    loop();
};

// @param {string} line The G-code line
const parseLine = (() => {
    // http://reprap.org/wiki/G-code#Special_fields
    // The checksum "cs" for a GCode string "cmd" (including its line number) is computed
    // by exor-ing the bytes in the string up to and not including the * character.
    const computeChecksum = (s) => {
        s = s || '';
        if (s.lastIndexOf('*') >= 0) {
            s = s.substr(0, s.lastIndexOf('*'));
        }

        let cs = 0;
        for (let i = 0; i < s.length; ++i) {
            const c = s[i].charCodeAt(0);
            cs = cs ^ c;
        }
        return cs;
    };
    const stripLine = (() => {
        const re1 = new RegExp(/\s*[%#;].*/g); // Strip everything after %, #, or ; to the end of the line, including preceding spaces
        const re2 = new RegExp(/\s*\(.*\)/g); // Remove anything inside the parentheses
        const re3 = new RegExp(/\s+/g);

        return (s => s.replace(re1, '').replace(re2, '').replace(re3, ''));
    })();
    const re = /([a-zA-Z][0-9\+\-\.]*)|(\*[0-9]+)/igm;

    return (line, options) => {
        options = options || {};
        options.noParseLine = options.noParseLine || false;

        const result = {
            line: line
        };

        if (!options.noParseLine) {
            result.words = [];

            let ln; // Line number
            let cs; // Checksum
            const words = stripLine(line).match(re) || [];
            for (let i = 0; i < words.length; ++i) {
                const word = words[i];
                const letter = word[0].toUpperCase();
                const argument = word.slice(1);

                //
                // Special fields
                //

                { // N: Line number
                    if (letter === 'N' && typeof ln === 'undefined') {
                        // Line (block) number in program
                        ln = Number(argument);
                        continue;
                    }
                }

                { // *: Checksum
                    if (letter === '*' && typeof cs === 'undefined') {
                        cs = Number(argument);
                        continue;
                    }
                }

                result.words.push([letter, Number(argument)]);
            }

            (typeof(ln) !== 'undefined') && (result.ln = ln); // Line number
            (typeof(cs) !== 'undefined') && (result.cs = cs); // Checksum
            if (result.cs && (computeChecksum(line) !== result.cs)) {
                result.err = true; // checksum failed
            }
        }

        return result;
    };
})();

// @param {object} stream The G-code line stream
// @param {options} options The options object
// @param {function} callback The callback function
const parseStream = (stream, options, callback = noop) => {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }

    const emitter = new events.EventEmitter();

    try {
        const results = [];
        stream
            .pipe(new GCodeLineStream(options))
            .on('data', (data) => {
                emitter.emit('data', data);
                results.push(data);
            })
            .on('end', () => {
                emitter.emit('end', results);
                callback && callback(null, results);
            })
            .on('error', callback);
    } catch (err) {
        callback(err);
    }

    return emitter;
};

// @param {string} file The G-code path name
// @param {options} options The options object
// @param {function} callback The callback function
const parseFile = (file, options, callback = noop) => {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    file = file || '';
    let s = fs.createReadStream(file, { encoding: 'utf8' });
    s.on('error', callback);
    return parseStream(s, options, callback);
};

const parseFileSync = (file, options) => {
    return parseStringSync(fs.readFileSync(file, 'utf8'), options);
};

// @param {string} str The G-code text string
// @param {options} options The options object
// @param {function} callback The callback function
const parseString = (str, options, callback = noop) => {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    return parseStream(streamify(str), options, callback);
};

const parseStringSync = (str, options) => {
    const { noParseLine = false } = { ...options };
    const results = [];
    const lines = str.split('\n');

    for (let i = 0; i < lines.length; ++i) {
        const line = lines[i].trim();
        if (line.length === 0) {
            continue;
        }
        const result = parseLine(line, { noParseLine });
        results.push(result);
    }

    return results;
};

// @param {string} str The G-code text string
// @param {options} options The options object

class GCodeLineStream extends Transform {
    state = {
        lineCount: 0,
        lastChunkEndedWithCR: false,
    };
    options = {
        batchSize: 1000,
        noParseLine: false
    };
    lineBuffer = '';
    re = new RegExp(/.*(?:\r\n|\r|\n)|.+$/g);

    // @param {object} [options] The options object
    // @param {number} [options.batchSize] The batch size.
    // @param {boolean} [options.noParseLine] True to not parse line, false otherwise.
    constructor(options = {}) {
        super({ objectMode: true });

        this.options = {
            ...this.options,
            ...options
        };
    }

    _transform(chunk, encoding, next) {
        // decode binary chunks as UTF-8
        encoding = encoding || 'utf8';

        if (Buffer.isBuffer(chunk)) {
            if (encoding === 'buffer') {
                encoding = 'utf8';
            }
            chunk = chunk.toString(encoding);
        }

        this.lineBuffer += chunk;

        if (!containsLineEnd(chunk)) {
            next();
            return;
        }

        const lines = this.lineBuffer.match(this.re);
        if (!lines || lines.length === 0) {
            next();
            return;
        }

        // Do not split CRLF which spans chunks
        if (this.state.lastChunkEndedWithCR && lines[0] === '\n') {
            lines.shift();
        }

        this.state.lastChunkEndedWithCR = (this.lineBuffer[this.lineBuffer.length - 1] === '\r');

        if ((this.lineBuffer[this.lineBuffer.length - 1] === '\r') ||
            (this.lineBuffer[this.lineBuffer.length - 1] === '\n')) {
            this.lineBuffer = '';
        } else {
            const line = lines.pop() || '';
            this.lineBuffer = line;
        }

        iterateArray(lines, { batchSize: this.options.batchSize }, (line, key) => {
            line = line.trim();
            if (line.length > 0) {
                const result = parseLine(line, {
                    noParseLine: this.options.noParseLine
                });
                this.push(result);
            }
        }, next);
    }
    _flush(done) {
        if (this.lineBuffer) {
            const line = this.lineBuffer.trim();
            if (line.length > 0) {
                const result = parseLine(line, {
                    noParseLine: this.options.noParseLine
                });
                this.push(result);
            }

            this.lineBuffer = '';
            this.state.lastChunkEndedWithCR = false;
        }

        done();
    }
}

export {
    GCodeLineStream,
    parseLine,
    parseStream,
    parseFile,
    parseFileSync,
    parseString,
    parseStringSync
};
<commit_msg>Fix a bug of removing comments inside the parentheses in stripComments<commit_after>import events from 'events';
import fs from 'fs';
import timers from 'timers';
import stream, { Transform } from 'stream';

const noop = () => {};

const streamify = (text) => {
    const s = new stream.Readable();
    s.push(text);
    s.push(null);
    return s;
};

const containsLineEnd = (() => {
    const re = new RegExp(/.*(?:\r\n|\r|\n)/g);

    return (s => !!s.match(re));
})();

// @param {array} arr The array to iterate over.
// @param {object} opts The options object.
// @param {function} iteratee The iteratee invoked per element.
// @param {function} done The done invoked after the loop has finished.
const iterateArray = (arr = [], opts = {}, iteratee = noop, done = noop) => {
    if (typeof opts === 'function') {
        done = iteratee;
        iteratee = opts;
        opts = {};
    }

    opts.batchSize = opts.batchSize || 1;

    const loop = (i = 0) => {
        for (let count = 0; i < arr.length && count < opts.batchSize; ++i, ++count) {
            iteratee(arr[i], i, arr);
        }
        if (i < arr.length) {
            timers.setImmediate(() => loop(i));
            return;
        }
        done();
    };
    loop();
};

// @param {string} line The G-code line
const parseLine = (() => {
    // http://reprap.org/wiki/G-code#Special_fields
    // The checksum "cs" for a GCode string "cmd" (including its line number) is computed
    // by exor-ing the bytes in the string up to and not including the * character.
    const computeChecksum = (s) => {
        s = s || '';
        if (s.lastIndexOf('*') >= 0) {
            s = s.substr(0, s.lastIndexOf('*'));
        }

        let cs = 0;
        for (let i = 0; i < s.length; ++i) {
            const c = s[i].charCodeAt(0);
            cs = cs ^ c;
        }
        return cs;
    };
    const stripComments = (() => {
        const re1 = new RegExp(/\s*[%#;].*/g); // Strip everything after %, #, or ; to the end of the line, including preceding spaces
        const re2 = new RegExp(/\s*\([^\)]*\)/g); // Remove anything inside the parentheses
        const re3 = new RegExp(/\s+/g);
        return (line => line.replace(re1, '').replace(re2, '').replace(re3, ''));
    })();
    const re = /([a-zA-Z][0-9\+\-\.]*)|(\*[0-9]+)/igm;

    return (line, options) => {
        options = options || {};
        options.noParseLine = options.noParseLine || false;

        const result = {
            line: line
        };

        if (!options.noParseLine) {
            result.words = [];

            let ln; // Line number
            let cs; // Checksum
            const words = stripComments(line).match(re) || [];
            for (let i = 0; i < words.length; ++i) {
                const word = words[i];
                const letter = word[0].toUpperCase();
                const argument = word.slice(1);

                //
                // Special fields
                //

                { // N: Line number
                    if (letter === 'N' && typeof ln === 'undefined') {
                        // Line (block) number in program
                        ln = Number(argument);
                        continue;
                    }
                }

                { // *: Checksum
                    if (letter === '*' && typeof cs === 'undefined') {
                        cs = Number(argument);
                        continue;
                    }
                }

                result.words.push([letter, Number(argument)]);
            }

            (typeof(ln) !== 'undefined') && (result.ln = ln); // Line number
            (typeof(cs) !== 'undefined') && (result.cs = cs); // Checksum
            if (result.cs && (computeChecksum(line) !== result.cs)) {
                result.err = true; // checksum failed
            }
        }

        return result;
    };
})();

// @param {object} stream The G-code line stream
// @param {options} options The options object
// @param {function} callback The callback function
const parseStream = (stream, options, callback = noop) => {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }

    const emitter = new events.EventEmitter();

    try {
        const results = [];
        stream
            .pipe(new GCodeLineStream(options))
            .on('data', (data) => {
                emitter.emit('data', data);
                results.push(data);
            })
            .on('end', () => {
                emitter.emit('end', results);
                callback && callback(null, results);
            })
            .on('error', callback);
    } catch (err) {
        callback(err);
    }

    return emitter;
};

// @param {string} file The G-code path name
// @param {options} options The options object
// @param {function} callback The callback function
const parseFile = (file, options, callback = noop) => {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    file = file || '';
    let s = fs.createReadStream(file, { encoding: 'utf8' });
    s.on('error', callback);
    return parseStream(s, options, callback);
};

const parseFileSync = (file, options) => {
    return parseStringSync(fs.readFileSync(file, 'utf8'), options);
};

// @param {string} str The G-code text string
// @param {options} options The options object
// @param {function} callback The callback function
const parseString = (str, options, callback = noop) => {
    if (typeof options === 'function') {
        callback = options;
        options = {};
    }
    return parseStream(streamify(str), options, callback);
};

const parseStringSync = (str, options) => {
    const { noParseLine = false } = { ...options };
    const results = [];
    const lines = str.split('\n');

    for (let i = 0; i < lines.length; ++i) {
        const line = lines[i].trim();
        if (line.length === 0) {
            continue;
        }
        const result = parseLine(line, { noParseLine });
        results.push(result);
    }

    return results;
};

// @param {string} str The G-code text string
// @param {options} options The options object

class GCodeLineStream extends Transform {
    state = {
        lineCount: 0,
        lastChunkEndedWithCR: false,
    };
    options = {
        batchSize: 1000,
        noParseLine: false
    };
    lineBuffer = '';
    re = new RegExp(/.*(?:\r\n|\r|\n)|.+$/g);

    // @param {object} [options] The options object
    // @param {number} [options.batchSize] The batch size.
    // @param {boolean} [options.noParseLine] True to not parse line, false otherwise.
    constructor(options = {}) {
        super({ objectMode: true });

        this.options = {
            ...this.options,
            ...options
        };
    }

    _transform(chunk, encoding, next) {
        // decode binary chunks as UTF-8
        encoding = encoding || 'utf8';

        if (Buffer.isBuffer(chunk)) {
            if (encoding === 'buffer') {
                encoding = 'utf8';
            }
            chunk = chunk.toString(encoding);
        }

        this.lineBuffer += chunk;

        if (!containsLineEnd(chunk)) {
            next();
            return;
        }

        const lines = this.lineBuffer.match(this.re);
        if (!lines || lines.length === 0) {
            next();
            return;
        }

        // Do not split CRLF which spans chunks
        if (this.state.lastChunkEndedWithCR && lines[0] === '\n') {
            lines.shift();
        }

        this.state.lastChunkEndedWithCR = (this.lineBuffer[this.lineBuffer.length - 1] === '\r');

        if ((this.lineBuffer[this.lineBuffer.length - 1] === '\r') ||
            (this.lineBuffer[this.lineBuffer.length - 1] === '\n')) {
            this.lineBuffer = '';
        } else {
            const line = lines.pop() || '';
            this.lineBuffer = line;
        }

        iterateArray(lines, { batchSize: this.options.batchSize }, (line, key) => {
            line = line.trim();
            if (line.length > 0) {
                const result = parseLine(line, {
                    noParseLine: this.options.noParseLine
                });
                this.push(result);
            }
        }, next);
    }
    _flush(done) {
        if (this.lineBuffer) {
            const line = this.lineBuffer.trim();
            if (line.length > 0) {
                const result = parseLine(line, {
                    noParseLine: this.options.noParseLine
                });
                this.push(result);
            }

            this.lineBuffer = '';
            this.state.lastChunkEndedWithCR = false;
        }

        done();
    }
}

export {
    GCodeLineStream,
    parseLine,
    parseStream,
    parseFile,
    parseFileSync,
    parseString,
    parseStringSync
};
<|endoftext|>


<commit_before>'use strict';

//////
// Imports
import React from 'react';
import ReactDOM from 'react-dom';
import request from 'request';
import settings from 'electron-settings';
import WebSocket from 'websocket';

import {formatPostUrl, formatSocketUrl} from './utils';

import Conversation from './components/Conversation';
import Prompt from './components/Prompt';
import ConnectionSettings from './components/ConnectionSettings';

//////
// Global variables
var active_connection = undefined;
var is_connected = false;
var host = settings.get("host", "localhost");
var port = settings.get("port", "8080");
var client = new WebSocket.client();
var connectionCooldown = 0;
var connectionTimeout = undefined;
var conversation = [];

//////
// Functions

class ChatClient extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      conversation: [],
    };
  }

  render(){
    return (

        <Conversation items={conversation} />
        <Prompt connection={is_connected} />
        <ConnectionSettings host={host} port={port} />

    );
  }
}

var render = function(){
  // Render the conversation
  ReactDOM.render(<ChatClient />,
    document.getElementById("wrapper")
  );
}

var addMessage = function(message, sender){
  // Add new message to the conversation
  conversation.push({
    "text": message,
    "user": sender,
    "time": new Date()
  });
  render();
}

var updateStatusIndicator = function(status){
  is_connected = status;
  render();
}

var sendUserMessage = function(){
  var user_message = document.getElementById("input").value;
  if (active_connection && active_connection.connected) {
    if (user_message != ""){
      document.getElementById("input").value = "";
      addMessage(user_message, "user");
      sendMessageToSocket(active_connection, user_message);
    }
  }
}

var connectToWebsocket = function() {
  request.post(formatPostUrl(host, port), function(error, response, body){
    if (error){
      console.log(error);
      reconnectToWebSocket();
    } else {
      var socket = JSON.parse(body)["socket"];
      client.connect(formatSocketUrl(host, port, socket));
    }
  })
}

var reconnectToWebSocket = function() {
  if (active_connection && active_connection.connected) {
    active_connection.close();
  }
  if (connectionTimeout){
    clearTimeout(connectionTimeout);
  }
  console.log(`Reconnecting in ${connectionCooldown} seconds.`);
  backoffCooldown();
  connectionTimeout = setTimeout(connectToWebsocket, connectionCooldown * 1000);
}

var reconnectToWebSocketImmediately = function() {
  resetCooldown();
  reconnectToWebSocket();
}

var resetCooldown = function() {
  connectionCooldown = 0;
}

var backoffCooldown = function(){
    if (connectionCooldown <1 ) {
      connectionCooldown = 1;
    } else if (connectionCooldown < 60){
      connectionCooldown = connectionCooldown * 2;
    }
}

var sendMessageToSocket = function(connection, message) {
  if (connection.connected) {
      connection.sendUTF(message);
  } else {
      console.log("Unable to send message");
  }
}

var handleSocketConnection = function(connection) {
  active_connection = connection;
  resetCooldown();
  console.log('WebSocket Client Connected');
  hideConnectionSettings();
  updateStatusIndicator(true);
  addMessage('connected', 'info');

  connection.on('error', handleSocketError);
  connection.on('close', handleSocketClose);
  connection.on('message', handleSocketMessage);
}

var handleSocketFailedConnection = function(error) {
  console.log('Connect Error: ' + error.toString());
  reconnectToWebSocket();
}

var handleSocketMessage = function(message) {
  if (message.type === 'utf8') {
      addMessage(message.utf8Data, "bot");
  }
}

var handleSocketClose = function() {
  console.log('echo-protocol Connection Closed');
  updateStatusIndicator(false);
  reconnectToWebSocket();
  addMessage('disconnected', 'info');
}

var handleSocketError = function(error) {
  console.log("Connection Error: " + error.toString());
  updateStatusIndicator(false);
  addMessage('connection error', 'info');
}

var checkForReturnInPrompt = function(event) {
  event.preventDefault();
  if (event.keyCode == 13) {
      sendUserMessage();
  }
}

var toggleConnectionSettings = function() {
  var connectionSettings = document.getElementById("connection-settings");
  connectionSettings.classList.toggle('active');
}

var hideConnectionSettings = function() {
  var connectionSettings = document.getElementById("connection-settings");
  connectionSettings.classList.remove('active');
}

var updateHost = function(event) {
  settings.set("host", event.target.value);
  host = settings.get("host", "localhost");
  reconnectToWebSocketImmediately();
}

var updatePort = function(event) {
  settings.set("port", event.target.value);
  host = settings.get("port", "localhost");
  reconnectToWebSocketImmediately();
}

//////
// First render of React components
render();

//////
// Event listeners
client.on('connect', handleSocketConnection);
client.on('connectFailed', handleSocketFailedConnection);
document.getElementById("send").addEventListener("click", sendUserMessage);
document.getElementById("input").addEventListener("keyup", checkForReturnInPrompt);
document.getElementById("status-indicator").addEventListener("click", toggleConnectionSettings);
document.getElementById("host").addEventListener("input", updateHost);
document.getElementById("port").addEventListener("input", updatePort);
document.getElementById("connect").addEventListener("click", reconnectToWebSocketImmediately);

//////
// Start
connectToWebsocket();
<commit_msg>Refactored all functionality into a React component<commit_after>'use strict';

//////
// Imports
import React from 'react';
import ReactDOM from 'react-dom';
import request from 'request';
import settings from 'electron-settings';
import WebSocket from 'websocket';

import {formatPostUrl, formatSocketUrl} from './utils';

import Conversation from './components/Conversation';
import Prompt from './components/Prompt';
import ConnectionSettings from './components/ConnectionSettings';

//////
// Functions

class ChatClient extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      conversation: [],
      connected: false,
    };

    this.active_connection = undefined;
    this.is_connected = false;
    this.host = settings.get("host", "localhost");
    this.port = settings.get("port", "8080");
    this.client = new WebSocket.client();
    this.connectionCooldown = 0;
    this.connectionTimeout = undefined;

    //////
    // Event listeners
    this.client.on('connect', this.handleSocketConnection);
    this.client.on('connectFailed', this.handleSocketFailedConnection);
    document.getElementById("send").addEventListener("click", this.sendUserMessage);
    document.getElementById("input").addEventListener("keyup", this.checkForReturnInPrompt);
    document.getElementById("status-indicator").addEventListener("click", this.toggleConnectionSettings);
    document.getElementById("host").addEventListener("input", this.updateHost);
    document.getElementById("port").addEventListener("input", this.updatePort);
    document.getElementById("connect").addEventListener("click", this.reconnectToWebSocketImmediately);

    this.connectToWebsocket();
  }

  render(){
    return (

        <Conversation items={this.conversation} />
        <Prompt connection={this.is_connected} />
        <ConnectionSettings host={this.host} port={this.port} />

    );
  }

  addMessage(message, sender){
    // Add new message to the conversation
    this.setState((prevState, props) => ({
      conversation: prevState.conversation.push({
        "text": message,
        "user": sender,
        "time": new Date()
      })
    }));
  }

  updateStatusIndicator(status){
    this.setState({connected: status});
  }

  sendUserMessage(){
    var user_message = document.getElementById("input").value;
    if (this.active_connection && this.active_connection.connected) {
      if (user_message != ""){
        document.getElementById("input").value = "";
        this.addMessage(user_message, "user");
        sendMessageToSocket(this.active_connection, user_message);
      }
    }
  }

  connectToWebsocket() {
    request.post(formatPostUrl(this.host, this.port), function(error, response, body){
      if (error){
        console.log(error);
        reconnectToWebSocket();
      } else {
        var socket = JSON.parse(body)["socket"];
        this.client.connect(formatSocketUrl(this.host, this.port, socket));
      }
    })
  }

  reconnectToWebSocket() {
    if (this.active_connection && this.active_connection.connected) {
      this.active_connection.close();
    }
    if (this.connectionTimeout){
      clearTimeout(this.connectionTimeout);
    }
    console.log(`Reconnecting in ${this.connectionCooldown} seconds.`);
    this.backoffCooldown();
    this.connectionTimeout = setTimeout(connectToWebsocket, this.connectionCooldown * 1000);
  }

  reconnectToWebSocketImmediately() {
    this.resetCooldown();
    this.reconnectToWebSocket();
  }

  resetCooldown() {
    this.connectionCooldown = 0;
  }

  backoffCooldown(){
      if (this.connectionCooldown <1 ) {
        this.connectionCooldown = 1;
      } else if (this.connectionCooldown < 60){
        this.connectionCooldown = this.connectionCooldown * 2;
      }
  }

  sendMessageToSocket(connection, message) {
    if (connection.connected) {
        connection.sendUTF(message);
    } else {
        console.log("Unable to send message");
    }
  }

  handleSocketConnection(connection) {
    this.active_connection = connection;
    this.resetCooldown();
    console.log('WebSocket Client Connected');
    this.hideConnectionSettings();
    this.updateStatusIndicator(true);
    this.addMessage('connected', 'info');

    connection.on('error', this.handleSocketError);
    connection.on('close', this.handleSocketClose);
    connection.on('message', this.handleSocketMessage);
  }

  handleSocketFailedConnection(error) {
    console.log('Connect Error: ' + error.toString());
    this.reconnectToWebSocket();
  }

  handleSocketMessage(message) {
    if (message.type === 'utf8') {
        this.addMessage(message.utf8Data, "bot");
    }
  }

  handleSocketClose() {
    console.log('echo-protocol Connection Closed');
    this.updateStatusIndicator(false);
    this.reconnectToWebSocket();
    this.addMessage('disconnected', 'info');
  }

  handleSocketError(error) {
    console.log("Connection Error: " + error.toString());
    this.updateStatusIndicator(false);
    this.addMessage('connection error', 'info');
  }

  checkForReturnInPrompt(event) {
    event.preventDefault();
    if (event.keyCode == 13) {
        this.sendUserMessage();
    }
  }

  toggleConnectionSettings() {
    var connectionSettings = document.getElementById("connection-settings");
    connectionSettings.classList.toggle('active');
  }

  hideConnectionSettings() {
    var connectionSettings = document.getElementById("connection-settings");
    connectionSettings.classList.remove('active');
  }

  updateHost(event) {
    settings.set("host", event.target.value);
    this.host = settings.get("host", "localhost");
    this.reconnectToWebSocketImmediately();
  }

  updatePort(event) {
    settings.set("port", event.target.value);
    this.port = settings.get("port", "localhost");
    this.reconnectToWebSocketImmediately();
  }
}

ReactDOM.render(<ChatClient />,
  document.getElementById("wrapper")
);
<|endoftext|>


<commit_before>import * as mobx 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 (!mobx)
  throw new Error(TARGET_LIB_NAME + ' requires the MobX package');
if (!React)
  throw new Error(TARGET_LIB_NAME + ' requires React to be available');

if (__TARGET__ === 'browser' && typeof rdBatched === "function")
  mobx.extras.setReactionScheduler(rdBatched);
if (__TARGET__ === 'native' && typeof rnBatched === "function")
  mobx.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 */
if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === 'object') {
  __MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobxReact(exports, mobx)
}
<commit_msg>Dropped using module.exports for devtools<commit_after>import * as mobx 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 (!mobx)
  throw new Error(TARGET_LIB_NAME + ' requires the MobX package');
if (!React)
  throw new Error(TARGET_LIB_NAME + ' requires React to be available');

if (__TARGET__ === 'browser' && typeof rdBatched === "function")
  mobx.extras.setReactionScheduler(rdBatched);
if (__TARGET__ === 'native' && typeof rnBatched === "function")
  mobx.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 mobxReact = { renderReporter, componentByNodeRegistery, trackComponents };
  __MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobxReact(mobxReact, mobx)
}
<|endoftext|>