<commit_before>#!/usr/bin/env node

'use strict';

/**
 * This file is meant to be linked as a "kbox" executable.
 */

var fs = require('fs');
var path = require('path');

var _ = require('lodash');
var S = require('string');
var argv = require('minimist')(process.argv.slice(2));
var async = require('async');
var chalk = require('chalk');
var Liftoff = require('liftoff');
var tildify = require('tildify');

var kbox = require('../lib/kbox.js');
var assert = require('assert');
var config = kbox.core.config;
var deps = kbox.core.deps;
var env = kbox.core.env;
var tasks = kbox.core.tasks;
var _util = kbox.util;
var shell = kbox.util.shell;

var initPlugins = function(globalConfig, callback) {
  var plugins = globalConfig.globalPlugins;
  async.eachSeries(plugins, function(plugin, next) {
    kbox.require(plugin, next);
  },
  function(err) {
    callback(err);
  });
};

var init = function(callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function');
  }

  // globalConfig
  var globalConfig = config.getGlobalConfig();
  deps.register('globalConfig', globalConfig);
  deps.register('config', globalConfig);
  // require
  deps.register('kboxRequire', kbox.require);
  // mode
  deps.register('mode', kbox.core.mode.set('cli'));
  // shell
  deps.register('shell', shell);
  // kbox
  deps.register('kbox', kbox);
  // events
  deps.register('events', kbox.core.events);
  // argv
  deps.register('argv', argv);
  // tasks
  tasks.init();
  deps.register('tasks', tasks);
  kbox.engine.init(globalConfig, function(err) {
    if (err) {
      return callback(err);
    }

    kbox.services.init(globalConfig, kbox, function(err) {
      if (err) {
        return callback(err);
      }

      deps.register('services', kbox.services);
      deps.register('engine', kbox.engine);
      callback(null, globalConfig);
    });
  });
};

var initWithApp = function(app) {
  // appConfig
  deps.register('appConfig', app.config);
  // app
  deps.register('app', app);
};

// set env var for ORIGINAL cwd before anything touches it
process.env.INIT_CWD = process.cwd();

var cli = new Liftoff({
  name: 'profile',
  configName: '.kalabox',
  extensions: {
    '.json': null
  },
  modulePath: path.resolve(__dirname, '../'),
  modulePackage: path.resolve(__dirname, '../package.json')
});

// exit with 0 or 1
var failed = false;
process.once('exit', function(code) {
  if (code === 0 && failed) {
    process.exit(1);
  }
});

var cliPackage = require('../package');
var versionFlag = argv.v || argv.version;
var tasksFlag = argv.T || argv.tasks;

function handleError(err) {
  var cancel = false;
  // This is just in case logging module has an issue.
  var timer = setTimeout(function() {
    if (!cancel) {
      throw err;
    }
  }, 5000);
  // Log error.
  kbox.core.log.error(err, function() {
    // Cancel safety timer and throw err.
    cancel = true;
    clearTimeout(timer);
    throw err;
  });
}

/*function processTask(env) {
  // Get dependencies.
  deps.call(function(tasks) {
    // Get app specific task.
    var appTask = (function() {
      if (!env.app) {
        return null;
      } else {
        var args = argv._.slice();
        if (args[0] !== env.app.name) {
          args.unshift(env.app.name);
        }
        return tasks.getTask(args);
      }
    })();
    // Get global task.
    var globalTask = (function() {
      var args = argv._.slice();
      if (env.app && args[0] === env.app.name) {
        return null;
      } else {
        return tasks.getTask(args);
      }
    })();
    // Figure out if the app specific task or the global task should be used.
    var result = (function() {
      var result = {
        task: null,
        hasGlobalConflict: false,
        args: null
      };
      if (appTask && appTask.task.task && globalTask && globalTask.task.task) {
        if (argv.g) {
          result.task = globalTask.task;
          result.args = globalTask.args;
        } else {
          result.task = appTask.task;
          result.hasGlobalConflict = true;
          result.args = appTask.args;
        }
      } else if (appTask) {
        result.task = appTask.task;
        result.args = appTask.args;
      } else if (globalTask) {
        result.task = globalTask.task;
        result.args = globalTask.args;
      }
      return result;
    })();
    // Display menu choices or run task.
    if (!result || !result.task || !result.task.task) {
      // Always print menu from base of task tree.
      //tasks.prettyPrint(result.task);
      tasks.prettyPrint(null);
      process.exit(1);
    } else {
      argv._ = result.args;
      result.task.task(function(err) {
        if (err) {
          handleError(err);
        }
      });
    }
  });
}*/

function getAppContextFromArgv(apps, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function: ' + callback);
  }

  callback(null, _.find(apps, function(app) {
    return app.name === argv._[0];
  }));
}

function getAppContextFromCwd(apps, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function: ' + callback);
  }

  var cwd = S(process.cwd());
  callback(null, _.find(apps, function(app) {
    var appRoot = app.config.appRoot;
    return cwd.startsWith(appRoot);
  }));
}

function getAppContextFromCwdConfig(apps, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function: ' + callback);
  }

  var cwd = process.cwd();
  var configFilepath = path.join(cwd, 'kalabox.json');
  if (fs.existsSync(configFilepath)) {
    var config = kbox.core.config.getAppConfig(null, cwd);
    kbox.app.create(config.appName, config, function(err, app) {
      callback(err, app);
    });
  } else {
    callback(null);
  }
}

function ensureAppNodeModulesInstalled(app, callback) {
  var appRoot = app.config.appRoot;
  var packageFilepath = path.join(appRoot, 'package.json');
  fs.exists(packageFilepath, function(packageFileExists) {
    if (packageFileExists) {
      fs.readFile(packageFilepath, function(err, data) {
        if (err) {
          callback(err);
        } else {
          var json = JSON.parse(data);
          if (json.dependencies) {
            var depCount = _.reduce(json.dependencies, function(count, x) {
              return count += 1;
            }, 0);
            if (depCount > 0) {
              var nodeModulesDir = path.join(appRoot, 'node_modules');
              fs.exists(nodeModulesDir, function(nodeModulesDirExists) {
                if (!nodeModulesDirExists) {
                  kbox.app.installPackages(appRoot, callback);
                } else {
                  callback();
                }
              });
            } else {
              callback();
            }
          } else {
            callback();
          }
        }
      });
    } else {
      callback();
    }
  });
}

function getAppContext(apps, callback) {
  // Find the app context.
  var funcs = [
    getAppContextFromArgv,
    getAppContextFromCwd,
    getAppContextFromCwdConfig
  ];
  async.reduce(funcs, null, function(answer, func, next) {
    if (answer) {
      next(null, answer);
    } else {
      func(apps, function(err, result) {
        next(err, result);
      });
    }

  },
  function(err, appContext) {
    if (err) {
      callback(err);
    } else if (appContext) {
      // If there is an app context, make sure it's node modules are installed.
      ensureAppNodeModulesInstalled(appContext, function(err) {
        callback(err, appContext);
      });
    } else {
      callback();
    }
  });
}

/*
 * Find the correct cli task and run it.
 */
function processTask(app) {

  // Grab cli args, but slice out the node and kbox.js args.
  var argv = process.argv.slice(2);

  // Search for a task with the app name.
  var appResult = (function() {
    if (!app) {
      return null;
    } else if (argv.length === 0) {
      return null;
    } else {
      var appArgv = argv.slice(0);
      if (!_.contains(argv, app.name)) {
        appArgv.unshift(app.name);
      }
      return kbox.tasks.find(appArgv);
    }
  })();

  // Search for a task without the app name.
  var globalResult = kbox.tasks.find(argv);

  // Is there a conflict between an app task and a global task?
  var conflict = !!appResult && !!globalResult;

  // For now just use the app result if it exists.
  var result = (function() {
    if (appResult) {
      return appResult;
    } else {
      return globalResult;
    }
  })();

  if (kbox.tasks.isTask(result)) {

    // We found a task, so run it.
    kbox.tasks.run(result, function(err) {

      // Whoops an error happened.
      if (err) {
        handleError(err);
      }

    });

  } else if (kbox.tasks.isBranch(result)) {

    // We ended up with a branch, so display task tree.
    kbox.tasks.showMenu(result);

  } else {

    // This should never happen.
    assert(false);

  }
}

function handleArguments(env) {
  var workingDir = env.cwd;
  var configPath = path.join(env.cwd, '.kalabox', 'profile.json');

  // Init dependencies.
  init(function(err, globalConfig) {
    if (err) {
      return handleError(err);
    }

    // Get full list of registered apps.
    kbox.app.list(function(err, apps) {
      if (err) {
        return handleError(err);
      }

      // Find the app context if there is one.
      getAppContext(apps, function(err, appContext) {
        if (err) {
          return handleError(err);
        }

        // Register the app context as a dependency.
        if (appContext) {
          env.app = appContext;
          initWithApp(appContext);
        }

        // Init global plugins.
        initPlugins(globalConfig, function(err) {
          if (err) {

            handleError(err);

          } else {

            if (appContext) {

              // Load app plugins for the app context app.
              kbox.app.loadPlugins(appContext, function(err) {
                if (err) {

                  handleError(err);

                } else {

                  // Run the task.
                  processTask(appContext);

                }
              });

            } else {

              // Run the task.
              processTask();

            }

          }
        });

      });
    });
  });

}

cli.on('require', function(name) {
  console.log('Requiring external module', chalk.magenta(name));
});

cli.on('requireFail', function(name) {
  console.log(chalk.red('Failed to load external module'), chalk.magenta(name));
});

cli.launch({
  cwd: argv.cwd,
  configPath: argv.kalaboxfile,
  require: argv.require,
  completion: argv.completion,
  verbose: argv.verbose,
  app: argv.app
}, handleArguments);
<commit_msg>Fixed small bug in cli menu logic, when an invalid command path was given.<commit_after>#!/usr/bin/env node

'use strict';

/**
 * This file is meant to be linked as a "kbox" executable.
 */

var fs = require('fs');
var path = require('path');

var _ = require('lodash');
var S = require('string');
var argv = require('minimist')(process.argv.slice(2));
var async = require('async');
var chalk = require('chalk');
var Liftoff = require('liftoff');
var tildify = require('tildify');

var kbox = require('../lib/kbox.js');
var assert = require('assert');
var config = kbox.core.config;
var deps = kbox.core.deps;
var env = kbox.core.env;
var tasks = kbox.core.tasks;
var _util = kbox.util;
var shell = kbox.util.shell;

var initPlugins = function(globalConfig, callback) {
  var plugins = globalConfig.globalPlugins;
  async.eachSeries(plugins, function(plugin, next) {
    kbox.require(plugin, next);
  },
  function(err) {
    callback(err);
  });
};

var init = function(callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function');
  }

  // globalConfig
  var globalConfig = config.getGlobalConfig();
  deps.register('globalConfig', globalConfig);
  deps.register('config', globalConfig);
  // require
  deps.register('kboxRequire', kbox.require);
  // mode
  deps.register('mode', kbox.core.mode.set('cli'));
  // shell
  deps.register('shell', shell);
  // kbox
  deps.register('kbox', kbox);
  // events
  deps.register('events', kbox.core.events);
  // argv
  deps.register('argv', argv);
  // tasks
  tasks.init();
  deps.register('tasks', tasks);
  kbox.engine.init(globalConfig, function(err) {
    if (err) {
      return callback(err);
    }

    kbox.services.init(globalConfig, kbox, function(err) {
      if (err) {
        return callback(err);
      }

      deps.register('services', kbox.services);
      deps.register('engine', kbox.engine);
      callback(null, globalConfig);
    });
  });
};

var initWithApp = function(app) {
  // appConfig
  deps.register('appConfig', app.config);
  // app
  deps.register('app', app);
};

// set env var for ORIGINAL cwd before anything touches it
process.env.INIT_CWD = process.cwd();

var cli = new Liftoff({
  name: 'profile',
  configName: '.kalabox',
  extensions: {
    '.json': null
  },
  modulePath: path.resolve(__dirname, '../'),
  modulePackage: path.resolve(__dirname, '../package.json')
});

// exit with 0 or 1
var failed = false;
process.once('exit', function(code) {
  if (code === 0 && failed) {
    process.exit(1);
  }
});

var cliPackage = require('../package');
var versionFlag = argv.v || argv.version;
var tasksFlag = argv.T || argv.tasks;

function handleError(err) {
  var cancel = false;
  // This is just in case logging module has an issue.
  var timer = setTimeout(function() {
    if (!cancel) {
      throw err;
    }
  }, 5000);
  // Log error.
  kbox.core.log.error(err, function() {
    // Cancel safety timer and throw err.
    cancel = true;
    clearTimeout(timer);
    throw err;
  });
}

/*function processTask(env) {
  // Get dependencies.
  deps.call(function(tasks) {
    // Get app specific task.
    var appTask = (function() {
      if (!env.app) {
        return null;
      } else {
        var args = argv._.slice();
        if (args[0] !== env.app.name) {
          args.unshift(env.app.name);
        }
        return tasks.getTask(args);
      }
    })();
    // Get global task.
    var globalTask = (function() {
      var args = argv._.slice();
      if (env.app && args[0] === env.app.name) {
        return null;
      } else {
        return tasks.getTask(args);
      }
    })();
    // Figure out if the app specific task or the global task should be used.
    var result = (function() {
      var result = {
        task: null,
        hasGlobalConflict: false,
        args: null
      };
      if (appTask && appTask.task.task && globalTask && globalTask.task.task) {
        if (argv.g) {
          result.task = globalTask.task;
          result.args = globalTask.args;
        } else {
          result.task = appTask.task;
          result.hasGlobalConflict = true;
          result.args = appTask.args;
        }
      } else if (appTask) {
        result.task = appTask.task;
        result.args = appTask.args;
      } else if (globalTask) {
        result.task = globalTask.task;
        result.args = globalTask.args;
      }
      return result;
    })();
    // Display menu choices or run task.
    if (!result || !result.task || !result.task.task) {
      // Always print menu from base of task tree.
      //tasks.prettyPrint(result.task);
      tasks.prettyPrint(null);
      process.exit(1);
    } else {
      argv._ = result.args;
      result.task.task(function(err) {
        if (err) {
          handleError(err);
        }
      });
    }
  });
}*/

function getAppContextFromArgv(apps, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function: ' + callback);
  }

  callback(null, _.find(apps, function(app) {
    return app.name === argv._[0];
  }));
}

function getAppContextFromCwd(apps, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function: ' + callback);
  }

  var cwd = S(process.cwd());
  callback(null, _.find(apps, function(app) {
    var appRoot = app.config.appRoot;
    return cwd.startsWith(appRoot);
  }));
}

function getAppContextFromCwdConfig(apps, callback) {
  if (typeof callback !== 'function') {
    throw new TypeError('Invalid callback function: ' + callback);
  }

  var cwd = process.cwd();
  var configFilepath = path.join(cwd, 'kalabox.json');
  if (fs.existsSync(configFilepath)) {
    var config = kbox.core.config.getAppConfig(null, cwd);
    kbox.app.create(config.appName, config, function(err, app) {
      callback(err, app);
    });
  } else {
    callback(null);
  }
}

function ensureAppNodeModulesInstalled(app, callback) {
  var appRoot = app.config.appRoot;
  var packageFilepath = path.join(appRoot, 'package.json');
  fs.exists(packageFilepath, function(packageFileExists) {
    if (packageFileExists) {
      fs.readFile(packageFilepath, function(err, data) {
        if (err) {
          callback(err);
        } else {
          var json = JSON.parse(data);
          if (json.dependencies) {
            var depCount = _.reduce(json.dependencies, function(count, x) {
              return count += 1;
            }, 0);
            if (depCount > 0) {
              var nodeModulesDir = path.join(appRoot, 'node_modules');
              fs.exists(nodeModulesDir, function(nodeModulesDirExists) {
                if (!nodeModulesDirExists) {
                  kbox.app.installPackages(appRoot, callback);
                } else {
                  callback();
                }
              });
            } else {
              callback();
            }
          } else {
            callback();
          }
        }
      });
    } else {
      callback();
    }
  });
}

function getAppContext(apps, callback) {
  // Find the app context.
  var funcs = [
    getAppContextFromArgv,
    getAppContextFromCwd,
    getAppContextFromCwdConfig
  ];
  async.reduce(funcs, null, function(answer, func, next) {
    if (answer) {
      next(null, answer);
    } else {
      func(apps, function(err, result) {
        next(err, result);
      });
    }

  },
  function(err, appContext) {
    if (err) {
      callback(err);
    } else if (appContext) {
      // If there is an app context, make sure it's node modules are installed.
      ensureAppNodeModulesInstalled(appContext, function(err) {
        callback(err, appContext);
      });
    } else {
      callback();
    }
  });
}

/*
 * Find the correct cli task and run it.
 */
function processTask(app) {

  // Grab cli args, but slice out the node and kbox.js args.
  var argv = process.argv.slice(2);

  // Search for a task with the app name.
  var appResult = (function() {
    if (!app) {
      return null;
    } else if (argv.length === 0) {
      return null;
    } else {
      var appArgv = argv.slice(0);
      if (!_.contains(argv, app.name)) {
        appArgv.unshift(app.name);
      }
      return kbox.tasks.find(appArgv);
    }
  })();

  // Search for a task without the app name.
  var globalResult = kbox.tasks.find(argv);

  // Is there a conflict between an app task and a global task?
  var conflict = !!appResult && !!globalResult;

  // For now just use the app result if it exists.
  var result = (function() {
    if (appResult) {
      return appResult;
    } else {
      return globalResult;
    }
  })();

  if (!result) {

    // We ended up with a branch, so display task tree.
    kbox.tasks.showMenu(kbox.tasks.getTaskTree());

  } else if (kbox.tasks.isBranch(result)) {

    // We ended up with a branch, so display task tree.
    kbox.tasks.showMenu(result);

  } else if (kbox.tasks.isTask(result)) {

    // We found a task, so run it.
    kbox.tasks.run(result, function(err) {

      // Whoops an error happened.
      if (err) {
        handleError(err);
      }

    });

  } else {

    // This should never happen.
    assert(false);

  }
}

function handleArguments(env) {
  var workingDir = env.cwd;
  var configPath = path.join(env.cwd, '.kalabox', 'profile.json');

  // Init dependencies.
  init(function(err, globalConfig) {
    if (err) {
      return handleError(err);
    }

    // Get full list of registered apps.
    kbox.app.list(function(err, apps) {
      if (err) {
        return handleError(err);
      }

      // Find the app context if there is one.
      getAppContext(apps, function(err, appContext) {
        if (err) {
          return handleError(err);
        }

        // Register the app context as a dependency.
        if (appContext) {
          env.app = appContext;
          initWithApp(appContext);
        }

        // Init global plugins.
        initPlugins(globalConfig, function(err) {
          if (err) {

            handleError(err);

          } else {

            if (appContext) {

              // Load app plugins for the app context app.
              kbox.app.loadPlugins(appContext, function(err) {
                if (err) {

                  handleError(err);

                } else {

                  // Run the task.
                  processTask(appContext);

                }
              });

            } else {

              // Run the task.
              processTask();

            }

          }
        });

      });
    });
  });

}

cli.on('require', function(name) {
  console.log('Requiring external module', chalk.magenta(name));
});

cli.on('requireFail', function(name) {
  console.log(chalk.red('Failed to load external module'), chalk.magenta(name));
});

cli.launch({
  cwd: argv.cwd,
  configPath: argv.kalaboxfile,
  require: argv.require,
  completion: argv.completion,
  verbose: argv.verbose,
  app: argv.app
}, handleArguments);
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const name = 'react-native';
const startRemoteDev = require('remotedev-server');
const injectDebugger = require('./injectDebugger');
const injectServer = require('./injectServer');
const bundleCode = fs.readFileSync(path.join(__dirname, '../bundle.js'), 'utf-8');

const getModulePath = moduleName =>
  path.join(process.cwd(), 'node_modules', moduleName);

const log = (pass, msg) => {
  const prefix = pass ? chalk.green.bgBlack('PASS') : chalk.red.bgBlack('FAIL');
  const color = pass ? chalk.blue : chalk.red;
  console.log(prefix, color(msg));
};

module.exports = argv => {
  const modulePath = getModulePath(argv.desktop ? 'react-native-desktop' : name);

  // Revert all injection
  if (argv.revert) {
    const passServ = injectServer.revert(modulePath);
    let msg = 'Revert injection of RemoteDev server from React Native local server';
    log(passServ, msg + (!passServ ? `, the file '${injectServer.path}' not found.` : '.'));

    const passDbg = injectDebugger.revert(modulePath);
    msg = 'Revert injection of RemoteDev monitor from React Native debugger';
    log(passDbg, msg + (!passDbg ? `, the file '${injectDebugger.path}' not found.` : '.'));

    if (!passServ || !passDbg) return false;
    return true;
  }

  // Inject server
  if (argv.injectserver) {
    let pass;
    if (argv.hostname || argv.port) {
      pass = injectServer.inject(modulePath, {
        hostname: argv.hostname,
        port: argv.port || 8000,
      });
    } else {
      pass = injectServer.inject(modulePath);
    }
    const msg = 'Inject RemoteDev server into React Native local server';
    log(pass, msg + (!pass ? `, the file '${injectServer.path}' not found.` : '.'));
    if (!pass) return false;
  }

  // Inject debugger
  if (argv.injectdebugger) {
    let pass;
    if (argv.hostname || argv.port) {
      pass = injectDebugger.inject(modulePath, bundleCode, {
        hostname: argv.hostname,
        port: argv.port || 8000,
        autoReconnect: true,
      });
    } else {
      pass = injectDebugger.inject(modulePath, bundleCode);
    }
    const msg = 'Inject RemoteDev monitor into React Native debugger';
    log(pass, msg + (!pass ? `, the file '${injectDebugger.path}' not found.` : '.'));
    if (!pass) return false;
  }

  // Run RemoteDev server
  if (argv.runserver) {
    return startRemoteDev(Object.assign({}, argv, { port: argv.port || 8000 }));
  }
  return true;
};
<commit_msg>Fix hostname default value<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const name = 'react-native';
const startRemoteDev = require('remotedev-server');
const injectDebugger = require('./injectDebugger');
const injectServer = require('./injectServer');
const bundleCode = fs.readFileSync(path.join(__dirname, '../bundle.js'), 'utf-8');

const getModulePath = moduleName =>
  path.join(process.cwd(), 'node_modules', moduleName);

const log = (pass, msg) => {
  const prefix = pass ? chalk.green.bgBlack('PASS') : chalk.red.bgBlack('FAIL');
  const color = pass ? chalk.blue : chalk.red;
  console.log(prefix, color(msg));
};

module.exports = argv => {
  const modulePath = getModulePath(argv.desktop ? 'react-native-desktop' : name);

  // Revert all injection
  if (argv.revert) {
    const passServ = injectServer.revert(modulePath);
    let msg = 'Revert injection of RemoteDev server from React Native local server';
    log(passServ, msg + (!passServ ? `, the file '${injectServer.path}' not found.` : '.'));

    const passDbg = injectDebugger.revert(modulePath);
    msg = 'Revert injection of RemoteDev monitor from React Native debugger';
    log(passDbg, msg + (!passDbg ? `, the file '${injectDebugger.path}' not found.` : '.'));

    if (!passServ || !passDbg) return false;
    return true;
  }

  // Inject server
  if (argv.injectserver) {
    let pass;
    if (argv.hostname || argv.port) {
      pass = injectServer.inject(modulePath, {
        hostname: argv.hostname || 'localhost',
        port: argv.port || 8000,
      });
    } else {
      pass = injectServer.inject(modulePath);
    }
    const msg = 'Inject RemoteDev server into React Native local server';
    log(pass, msg + (!pass ? `, the file '${injectServer.path}' not found.` : '.'));
    if (!pass) return false;
  }

  // Inject debugger
  if (argv.injectdebugger) {
    let pass;
    if (argv.hostname || argv.port) {
      pass = injectDebugger.inject(modulePath, bundleCode, {
        hostname: argv.hostname || 'localhost',
        port: argv.port || 8000,
        autoReconnect: true,
      });
    } else {
      pass = injectDebugger.inject(modulePath, bundleCode);
    }
    const msg = 'Inject RemoteDev monitor into React Native debugger';
    log(pass, msg + (!pass ? `, the file '${injectDebugger.path}' not found.` : '.'));
    if (!pass) return false;
  }

  // Run RemoteDev server
  if (argv.runserver) {
    return startRemoteDev(Object.assign({}, argv, { port: argv.port || 8000 }));
  }
  return true;
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * # nodegame-server make script
 *
 */

/**
 * Module dependencies.
 */

var program = require('commander'),
smoosh = require('smoosh'),
os = require('os'),
fs = require('fs'),
util = require('util'),
exec = require('child_process').exec,
path = require('path'),
J = require('JSUS').JSUS;

var info = require('./info.js');

var version = info.version;

var ngcDir = info.modulesDir.client;
var JSUSDir = info.modulesDir.JSUS;
var NDDBDir = info.modulesDir.NDDB;
var shelfDir = info.modulesDir.shelf;
var ngwindowDir = info.modulesDir.window;
var ngwidgetsDir = info.modulesDir.widgets;

var build_client = info.build.client;
var build_client_support = info.build.clientSupport;
var build_JSUS = info.build.JSUS;
var build_NDDB = info.build.NDDB;
var build_shelf = info.build.shelf;
var build_ngwindow = info.build.window;
var build_ngwidgets = info.build.widgets;

var rootDir = info.serverDir.root;
var buildDir = info.serverDir.build;
var cssDir = info.serverDir.css;
var libDir = info.serverDir.lib;
var confDir = info.serverDir.conf;

var buildDir_client = ngcDir + 'build/';
var buildDir_JSUS = JSUSDir + 'build/';
var buildDir_NDDB = NDDBDir + 'build/';
var buildDir_shelf = shelfDir + 'build/';
var buildDir_ngWindow = ngwindowDir + 'build/';
var buildDir_ngWidgets = ngwidgetsDir + 'build/';

// var ngcDir = J.resolveModuleDir('nodegame-client', __dirname);
// var JSUSDir = J.resolveModuleDir('JSUS', __dirname);
// var NDDBDir = J.resolveModuleDir('NDDB', __dirname);
// var shelfDir = J.resolveModuleDir('shelf.js', __dirname);
// var ngwindowDir = J.resolveModuleDir('nodegame-window', __dirname);
// var ngwidgetsDir = J.resolveModuleDir('nodegame-widgets', __dirname);
//
// var build_client = require(ngcDir + 'bin/build.js').build;
// var build_client_support = require(ngcDir + 'bin/build.js').build_support;
// var build_JSUS = require(JSUSDir + 'bin/build.js').build;
// var build_NDDB = require(NDDBDir + 'bin/build.js').build;
// var build_shelf = require(shelfDir + 'bin/build.js').build;
// var build_ngwindow = require(ngwindowDir + 'bin/build.js').build;
// var build_ngwidgets = require(ngwidgetsDir + 'bin/build.js').build;

// var rootDir = path.resolve(__dirname, '..');
// var buildDir = rootDir + '/public/javascripts/';
// var cssDir = rootDir + '/public/stylesheets/';
// var libDir = rootDir + '/lib/';
// var confDir = rootDir + '/conf/';

program
    .version(version);

program
    .command('clean')
    .description('Removes all files from build folder')
    .action(function(){
        J.cleandDir(buildDir);
    });

program
    .command('build-client [options]')
    .description('Creates a nodegame-client custom build')
// TODO: keep options update with client, or find a way to use the options
// defined in the client
    .option('-B, --bare', 'bare naked nodegame-client (no dependencies)')
    .option('-J, --JSUS', 'with JSUS')
    .option('-N, --NDDB', 'with NDDB')
    .option('-W, --window', 'with nodeGame-window')
    .option('-w, --widgets', 'with nodeGame-widgets')
    .option('-d, --addons', 'with nodeGame-client addons')
    .option('-s, --shelf', 'with Shelf.js')
    .option('-e, --es5', 'with support for old browsers')
    .option('-a, --all', 'full build of nodeGame-client')
    .option('-C, --clean', 'clean build directory')
    .option('-A, --analyse', 'analyse build')
    .option('-o, --output <file>', 'output file (without .js)')
    .action(function(env, options) {
        if (options.output && path.extname(options.output) === '.js') {
            options.output = path.basename(options.output, '.js');
        }
        options.clean = true;
        build_client(options);
        J.copyFromDir(buildDir_client, buildDir, '.js');
    });

program
    .command('multibuild')
    .description('Builds a set of javascript libraries for nodeGame')
    .action(function(){
        console.log('Multi-build for nodegame-server v.' + version);

        // nodegame-client
        build_client({
            clean: true,
            all: true,
            output: "nodegame-full",
        });
        build_client({
            bare: true,
            output: "nodegame-bare",
        });
        build_client({
            output: "nodegame",
        });
        //              build_client_support({
        //                      all: true,
        //              });

        J.copyFromDir(buildDir_client, buildDir, '.js');

        // JSUS
        build_JSUS({
            lib: ['obj','array','eval','parse','random','time','dom'],
            clean: true,
        });

        J.copyFromDir(buildDir_JSUS, buildDir, '.js');

        // NDDB
        build_NDDB({
            clean: true,
            all: true,
            output: "nddb-full",
        });
        build_NDDB({
            bare: true,
            output: "nddb-bare",
        });
        build_NDDB({
            JSUS: true,
            output: "nddb",
        });

        J.copyFromDir(buildDir_NDDB, buildDir, '.js');

        // Shelf.js
        build_shelf({
            clean: true,
            all: true,
            output: "shelf-full",
        });
        build_shelf({
            lib: ['amplify','cookie'],
            output: "shelf-browser",
        });
        build_shelf({
            lib: ['amplify'],
            output: "shelf-amplify",
        });
        build_shelf({
            lib: ['cookie'],
            output: "shelf-cookie",
        });
        build_shelf({
            lib: ['fs'],
            output: "shelf-fs",
        });

        J.copyFromDir(buildDir_shelf, buildDir, '.js');

        // ng-widgets
        build_ngwidgets({
            all: true,
            clean: true,
        });

        J.copyFromDir(buildDir_ngWidgets, buildDir, '.js');

        // ng-window
        build_ngwindow({
            all: true,
            clean: true,
        });

        J.copyFromDir(buildDir_ngWindow, buildDir, '.js');

        console.log('All javascript files built and copied to ' +
                    'public/javascript/');
    });

program
    .command('build-css')
    .description('Populates public/stylesheets/ and builds nodegame.css')
    .option('-o, --output <file>', 'output file (without .css)')
    .option('-A, --analyse', 'analyse build')
    .option('-C, --clean', 'clean CSS directory')
    .action(function(options) {
        var config, runIt, out, smooshed;

        function copyAndBuild() {
            // Copy CSS files from submodules.
            copyCSS(null, function() {
                // Set output name.
                out = options.output || "nodegame";
                if (path.extname(out) === '.css') {
                    out = path.basename(out, '.css');
                }

                // Configurations for file smooshing.
                config = {
                    // VERSION: version,

                    // Use JSHINT to spot code irregularities.
                    JSHINT_OPTS: {
                        boss: true,
                        forin: true,
                        browser: true,
                    },

                    CSS: {
                        // Need the extra slash.
                        DIST_DIR: '/' + cssDir
                    }
                };

                config.CSS[out] = [
                    cssDir + 'window.css',
                    cssDir + 'widgets.css'
                ];
                smooshed = smoosh.config(config);

                // Builds both uncompressed and compressed files.
                smooshed.build();

                if (options.analyse) {
                    smooshed.run(); // runs jshint on full build
                    smooshed.analyze(); // analyzes everything
                }

                console.log('All CSS files copied, and ' +
                            out + '.css created.');
            });
        }

        // Removes all files from the CSS folder.
        if (options.clean) {
            J.cleanDir(cssDir, '.css', copyAndBuild);
        }
        else {
            copyAndBuild();
        }

    });

program
    .command('refresh')
    .option('-a, --all', 'copy all js and css files')
    .option('-j, --js-only', 'copy only js files')
    .option('-c, --css-only', 'copy only css files')
    .description('Copies all .js .css files from submodules into public/')
    .action(function(options) {
        if (!options.all && !options.js && !options.css) {
            options.all = true;
        }

        if (options.all || options.js) {
            try {
                J.copyFromDir(buildDir_client, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find nodegame-client.');
            }
            try {
                J.copyFromDir(buildDir_ngWindow, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find nodegame-window.');
            }
            try {
                J.copyFromDir(buildDir_ngWidgets, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find nodegame-widgets.');
            }
            try {
                J.copyFromDir(buildDir_JSUS, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find JSUS.');
            }
            try {
                J.copyFromDir(buildDir_NDDB, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find NDDB.');
            }
            try {
                J.copyFromDir(buildDir_shelf, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find shelf.js.');
            }
        }

        if (options.all || options.css) {
            copyCSS();
        }

        console.log('All files copied to public/');
    });

program
    .command('doc')
    .description('Builds documentation files')
    .action(function(){
        console.log('Building documentation for nodegame-server v.' + version);
        // http://nodejs.org/api.html#_child_processes
        try {
            var dockerDir = J.resolveModuleDir('docker', rootDir);
        }
        catch(e) {
            console.log('module Docker not found. Cannot build doc.');
            console.log('Do \'npm install docker\' to install it.');
            return false;
        }
        var command = dockerDir + 'docker -i ' + rootDir +
            ' index.js lib/ -o ' + rootDir + '/docs/ -u';
        var child = exec(command, function (error, stdout, stderr) {
            if (stdout) console.log(stdout);
            if (stderr) console.log(stderr);
            if (error !== null) {
                console.log('build error: ' + error);
            }
        });
    });

program
    .command('sync <path> [options]')
    .description('Sync with the specified target directory (must exists)')
    .option('-a, --all', 'sync /lib and /conf folders (default)')
    .option('-l, --lib', 'sync the /lib folder')
    .option('-c, --conf', 'sync the /conf folder')
    .action(function(path, options) {

        if ('undefined' === typeof options) {
            options = {};
            options.all = true;
        }

        if (options.all) {
            copyDirTo(confDir, path + '/conf/');
            copyDirTo(libDir, path + '/lib/');
        }

        else if (options.conf) {
            copyDirTo(confDir, path);
        }

        else if (options.lib) {
            copyDirTo(libDir, path);
        }

        console.log('Done.');

    });

function copyDirTo(inputDir, targetDir) {

    if (!targetDir) {
        console.log('You must specify a target directory ' +
                    'for the \'sync\' command');
        return;
    }

    targetDir = path.resolve(targetDir);

    if (!fs.existsSync(targetDir)) {
        console.log(targetDir + ' does not exist');
        return false;
    }

    var stats = fs.lstatSync(targetDir);
    if (!stats.isDirectory()) {
        console.log(targetDir + ' is not a directory');
        return false;
    }

    targetDir = targetDir + '/';

    console.log('nodegame-server v.' + version + ': syncing ' + inputDir +
                ' with ' + targetDir);

    J.copyDirSyncRecursive(inputDir, targetDir);
}

function copyCSS(options, cb) {
    var asyncQueue = new J.getQueue();
    try {
        asyncQueue.add('window');
        J.copyFromDir(ngwindowDir + 'css/', cssDir, '.css', function() {
            asyncQueue.remove('window');
        });
    }
    catch(e) {
        console.log('make refresh: could not find nodegame-window css dir.');
        asyncQueue.remove('window');
    }
    try {
        asyncQueue.add('widgets');
        J.copyFromDir(ngwidgetsDir + 'css/', cssDir, '.css', function() {
            asyncQueue.remove('widgets');
        });
    }
    catch(e) {
        console.log('make refresh: could not find nodegame-window css dir.');
        asyncQueue.remove('widgets');
    }

    if (cb) {
        asyncQueue.onReady(cb);
    }
}

//Parsing options
program.parse(process.argv);
<commit_msg>minor<commit_after>#!/usr/bin/env node

/**
 * # nodegame-server make script
 *
 */

/**
 * Module dependencies.
 */

var program = require('commander'),
smoosh = require('smoosh'),
os = require('os'),
fs = require('fs'),
util = require('util'),
exec = require('child_process').exec,
path = require('path'),
J = require('JSUS').JSUS;

var info = require('./info.js');

var version = info.version;

var ngcDir = info.modulesDir.client;
var JSUSDir = info.modulesDir.JSUS;
var NDDBDir = info.modulesDir.NDDB;
var shelfDir = info.modulesDir.shelf;
var ngwindowDir = info.modulesDir.window;
var ngwidgetsDir = info.modulesDir.widgets;

var build_client = info.build.client;
var build_client_support = info.build.clientSupport;
var build_JSUS = info.build.JSUS;
var build_NDDB = info.build.NDDB;
var build_shelf = info.build.shelf;
var build_ngwindow = info.build.window;
var build_ngwidgets = info.build.widgets;

var rootDir = info.serverDir.root;
var buildDir = info.serverDir.build;
var cssDir = info.serverDir.css;
var libDir = info.serverDir.lib;
var confDir = info.serverDir.conf;

var buildDir_client = ngcDir + 'build/';
var buildDir_JSUS = JSUSDir + 'build/';
var buildDir_NDDB = NDDBDir + 'build/';
var buildDir_shelf = shelfDir + 'build/';
var buildDir_ngWindow = ngwindowDir + 'build/';
var buildDir_ngWidgets = ngwidgetsDir + 'build/';

program
    .version(version);

program
    .command('clean')
    .description('Removes all files from build folder')
    .action(function(){
        J.cleandDir(buildDir);
    });

program
    .command('build-client [options]')
    .description('Creates a nodegame-client custom build')
// TODO: keep options update with client, or find a way to use the options
// defined in the client
    .option('-B, --bare', 'bare naked nodegame-client (no dependencies)')
    .option('-J, --JSUS', 'with JSUS')
    .option('-N, --NDDB', 'with NDDB')
    .option('-W, --window', 'with nodeGame-window')
    .option('-w, --widgets', 'with nodeGame-widgets')
    .option('-d, --addons', 'with nodeGame-client addons')
    .option('-s, --shelf', 'with Shelf.js')
    .option('-e, --es5', 'with support for old browsers')
    .option('-a, --all', 'full build of nodeGame-client')
    .option('-C, --clean', 'clean build directory')
    .option('-A, --analyse', 'analyse build')
    .option('-o, --output <file>', 'output file (without .js)')
    .action(function(env, options) {
        if (options.output && path.extname(options.output) === '.js') {
            options.output = path.basename(options.output, '.js');
        }
        options.clean = true;
        build_client(options);
        J.copyFromDir(buildDir_client, buildDir, '.js');
    });

program
    .command('multibuild')
    .description('Builds a set of javascript libraries for nodeGame')
    .action(function(){
        console.log('Multi-build for nodegame-server v.' + version);

        // nodegame-client
        build_client({
            clean: true,
            all: true,
            output: "nodegame-full",
        });
        build_client({
            bare: true,
            output: "nodegame-bare",
        });
        build_client({
            output: "nodegame",
        });
        //              build_client_support({
        //                      all: true,
        //              });

        J.copyFromDir(buildDir_client, buildDir, '.js');

        // JSUS
        build_JSUS({
            lib: ['obj','array','eval','parse','random','time','dom'],
            clean: true,
        });

        J.copyFromDir(buildDir_JSUS, buildDir, '.js');

        // NDDB
        build_NDDB({
            clean: true,
            all: true,
            output: "nddb-full",
        });
        build_NDDB({
            bare: true,
            output: "nddb-bare",
        });
        build_NDDB({
            JSUS: true,
            output: "nddb",
        });

        J.copyFromDir(buildDir_NDDB, buildDir, '.js');

        // Shelf.js
        build_shelf({
            clean: true,
            all: true,
            output: "shelf-full",
        });
        build_shelf({
            lib: ['amplify','cookie'],
            output: "shelf-browser",
        });
        build_shelf({
            lib: ['amplify'],
            output: "shelf-amplify",
        });
        build_shelf({
            lib: ['cookie'],
            output: "shelf-cookie",
        });
        build_shelf({
            lib: ['fs'],
            output: "shelf-fs",
        });

        J.copyFromDir(buildDir_shelf, buildDir, '.js');

        // ng-widgets
        build_ngwidgets({
            all: true,
            clean: true,
        });

        J.copyFromDir(buildDir_ngWidgets, buildDir, '.js');

        // ng-window
        build_ngwindow({
            all: true,
            clean: true,
        });

        J.copyFromDir(buildDir_ngWindow, buildDir, '.js');

        console.log('All javascript files built and copied to ' +
                    'public/javascript/');
    });

program
    .command('build-css')
    .description('Populates public/stylesheets/ and builds nodegame.css')
    .option('-o, --output <file>', 'output file (without .css)')
    .option('-A, --analyse', 'analyse build')
    .option('-C, --clean', 'clean CSS directory')
    .action(function(options) {
        var config, runIt, out, smooshed;

        function copyAndBuild() {
            // Copy CSS files from submodules.
            copyCSS(null, function() {
                // Set output name.
                out = options.output || "nodegame";
                if (path.extname(out) === '.css') {
                    out = path.basename(out, '.css');
                }

                // Configurations for file smooshing.
                config = {
                    // VERSION: version,

                    // Use JSHINT to spot code irregularities.
                    JSHINT_OPTS: {
                        boss: true,
                        forin: true,
                        browser: true,
                    },

                    CSS: {
                        // Need the extra slash.
                        DIST_DIR: '/' + cssDir
                    }
                };

                config.CSS[out] = [
                    cssDir + 'window.css',
                    cssDir + 'widgets.css'
                ];
                smooshed = smoosh.config(config);

                // Builds both uncompressed and compressed files.
                smooshed.build();

                if (options.analyse) {
                    smooshed.run(); // runs jshint on full build
                    smooshed.analyze(); // analyzes everything
                }

                console.log('All CSS files copied, and ' +
                            out + '.css created.');
            });
        }

        // Removes all files from the CSS folder.
        if (options.clean) {
            J.cleanDir(cssDir, '.css', copyAndBuild);
        }
        else {
            copyAndBuild();
        }

    });

program
    .command('refresh')
    .option('-a, --all', 'copy all js and css files')
    .option('-j, --js-only', 'copy only js files')
    .option('-c, --css-only', 'copy only css files')
    .description('Copies all .js .css files from submodules into public/')
    .action(function(options) {
        if (!options.all && !options.js && !options.css) {
            options.all = true;
        }

        if (options.all || options.js) {
            try {
                J.copyFromDir(buildDir_client, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find nodegame-client.');
            }
            try {
                J.copyFromDir(buildDir_ngWindow, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find nodegame-window.');
            }
            try {
                J.copyFromDir(buildDir_ngWidgets, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find nodegame-widgets.');
            }
            try {
                J.copyFromDir(buildDir_JSUS, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find JSUS.');
            }
            try {
                J.copyFromDir(buildDir_NDDB, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find NDDB.');
            }
            try {
                J.copyFromDir(buildDir_shelf, buildDir, '.js');
            }
            catch(e) {
                console.log('make refresh: could not find shelf.js.');
            }
        }

        if (options.all || options.css) {
            copyCSS();
        }

        console.log('All files copied to public/');
    });

program
    .command('doc')
    .description('Builds documentation files')
    .action(function(){
        console.log('Building documentation for nodegame-server v.' + version);
        // http://nodejs.org/api.html#_child_processes
        try {
            var dockerDir = J.resolveModuleDir('docker', rootDir);
        }
        catch(e) {
            console.log('module Docker not found. Cannot build doc.');
            console.log('Do \'npm install docker\' to install it.');
            return false;
        }
        var command = dockerDir + 'docker -i ' + rootDir +
            ' index.js lib/ -o ' + rootDir + '/docs/ -u';
        var child = exec(command, function (error, stdout, stderr) {
            if (stdout) console.log(stdout);
            if (stderr) console.log(stderr);
            if (error !== null) {
                console.log('build error: ' + error);
            }
        });
    });

program
    .command('sync <path> [options]')
    .description('Sync with the specified target directory (must exists)')
    .option('-a, --all', 'sync /lib and /conf folders (default)')
    .option('-l, --lib', 'sync the /lib folder')
    .option('-c, --conf', 'sync the /conf folder')
    .action(function(path, options) {

        if ('undefined' === typeof options) {
            options = {};
            options.all = true;
        }

        if (options.all) {
            copyDirTo(confDir, path + '/conf/');
            copyDirTo(libDir, path + '/lib/');
        }

        else if (options.conf) {
            copyDirTo(confDir, path);
        }

        else if (options.lib) {
            copyDirTo(libDir, path);
        }

        console.log('Done.');

    });

function copyDirTo(inputDir, targetDir) {

    if (!targetDir) {
        console.log('You must specify a target directory ' +
                    'for the \'sync\' command');
        return;
    }

    targetDir = path.resolve(targetDir);

    if (!fs.existsSync(targetDir)) {
        console.log(targetDir + ' does not exist');
        return false;
    }

    var stats = fs.lstatSync(targetDir);
    if (!stats.isDirectory()) {
        console.log(targetDir + ' is not a directory');
        return false;
    }

    targetDir = targetDir + '/';

    console.log('nodegame-server v.' + version + ': syncing ' + inputDir +
                ' with ' + targetDir);

    J.copyDirSyncRecursive(inputDir, targetDir);
}

function copyCSS(options, cb) {
    var asyncQueue = new J.getQueue();
    try {
        asyncQueue.add('window');
        J.copyFromDir(ngwindowDir + 'css/', cssDir, '.css', function() {
            asyncQueue.remove('window');
        });
    }
    catch(e) {
        console.log('make refresh: could not find nodegame-window css dir.');
        asyncQueue.remove('window');
    }
    try {
        asyncQueue.add('widgets');
        J.copyFromDir(ngwidgetsDir + 'css/', cssDir, '.css', function() {
            asyncQueue.remove('widgets');
        });
    }
    catch(e) {
        console.log('make refresh: could not find nodegame-window css dir.');
        asyncQueue.remove('widgets');
    }

    if (cb) {
        asyncQueue.onReady(cb);
    }
}

//Parsing options
program.parse(process.argv);
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var jsonp = require('jsonp');
var jquery = require('jquery');
var scrollTo = require('scroll-to');
var offset = require('offset');
var base = "https://docs.auth0.com";
var hasInterpolation = require('reactive/lib/utils').hasInterpolation;
var interpolate = require('reactive/lib/utils').interpolate;
var jsonpOpts = {
  prefix: '__a0tn'
}

exports['tutorial-fetch'] = tutorialFetch;
exports['empty-if'] = emptyIf;
exports['remove-if'] = removeIf;
exports['disabled-if'] = disabledIf;
exports['scroll-on'] = scrollOn;

function tutorialFetch(el, property) {
  var binding = this;
  binding.change(function(url) {
    if (!url) return;

    var cancel = jsonp(base+url+'?e=1', jsonpOpts, ontutorial);

    function ontutorial(err, data) {
      if (err) return;
      if (!data.html) return;
      // jquery executes inline


<commit_before>var PluginInstance = require(__core + "PluginInstance.js");
let EventHandler = require(__core + "Plugin/EventHandler.js");

var plugAPI = require("plugapi"); //TODO: remove

export default class Playlist extends PluginInstance {
    init () {
        this.plug = this.manager.getPlugin("plug").plug; //TODO: implement better method
        this.subcommands = new SubCommands(this);
    }

    @EventHandler()
    command_playlist(message) {
        if (message.from.rank >= this.core.ranks.MANAGER) {
            var subcommand = message.args[0];
            var func = this.subcommands.list;
            if (this.subcommands[subcommand]) {
                func = this.subcommands[subcommand];
            }
            func.call(this.subcommands, message);
        }
        else {
            message.from.sendReply("Command only available to staff", {emote: true});
        }
    }

    @EventHandler()
    command_grab(message) {
        if (message.from.rank >= this.core.ranks.MANAGER) {
            this.plug.grab();
        }
        else {
            message.from.sendReply("Command only available to staff", {emote: true});
        }
    }
}

class SubCommands {
    constructor(plugin) {
        this.playlist = plugin;
    }

    list(message) {
        this.playlist.plug.getPlaylists(function(err, playlists) {
            var list = [];
            for (var i in playlists) {
                if (!playlists.hasOwnProperty(i)) {
                    continue;
                }
                let playlist = playlists[i];
                var entry = playlist.id + "("+playlist.name+")";
                if (playlist.active) {
                    entry = "*" + entry + "*";
                }
                list.push(entry);
            }
            message.from.sendReply(list.join(", "));
        });
    }

    activate(message) {
        this.playlist.plug.activatePlaylist(message.args[1], function (err, data) {
            if (err) {
                message.from.sendReply("Error: "+err.message);
            }
            else {
                message.from.sendReply("Playlist activated");
            }
        });
    }

    ["delete"](message) {
        this.playlist.plug.deletePlaylist(message.args[1], function(err, data) {
            if (err) {
                message.from.sendReply("Error: "+err.message);
            }
            else {
                message.from.sendReply("Playlist deleted");
            }
        });
    }

    add(message) {
        this.playlist.plug.createPlaylist(message.args[1], function(err, data) {
          if (err) {
              message.from.sendReply("Error: "+err.message);
          }
          else {
              message.from.sendReply("Playlist created, ID: "+data[0].id);
          }
      });
    }
}<commit_msg>grab requires playlist id :(<commit_after>var PluginInstance = require(__core + "PluginInstance.js");
let EventHandler = require(__core + "Plugin/EventHandler.js");

var plugAPI = require("plugapi"); //TODO: remove

export default class Playlist extends PluginInstance {
    init () {
        this.plug = this.manager.getPlugin("plug").plug; //TODO: implement better method
        this.subcommands = new SubCommands(this);
    }

    @EventHandler()
    command_playlist(message) {
        if (message.from.rank >= this.core.ranks.MANAGER) {
            var subcommand = message.args[0];
            var func = this.subcommands.list;
            if (this.subcommands[subcommand]) {
                func = this.subcommands[subcommand];
            }
            func.call(this.subcommands, message);
        }
        else {
            message.from.sendReply("Command only available to staff", {emote: true});
        }
    }

    @EventHandler()
    command_grab(message) {
        if (message.from.rank >= this.core.ranks.MANAGER) {
            this.plug.grab(parseInt(message.args[0]));
        }
        else {
            message.from.sendReply("Command only available to staff", {emote: true});
        }
    }
}

class SubCommands {
    constructor(plugin) {
        this.playlist = plugin;
    }

    list(message) {
        this.playlist.plug.getPlaylists(function(err, playlists) {
            var list = [];
            for (var i in playlists) {
                if (!playlists.hasOwnProperty(i)) {
                    continue;
                }
                let playlist = playlists[i];
                var entry = playlist.id + "("+playlist.name+")";
                if (playlist.active) {
                    entry = "*" + entry + "*";
                }
                list.push(entry);
            }
            message.from.sendReply(list.join(", "));
        });
    }

    activate(message) {
        this.playlist.plug.activatePlaylist(message.args[1], function (err, data) {
            if (err) {
                message.from.sendReply("Error: "+err.message);
            }
            else {
                message.from.sendReply("Playlist activated");
            }
        });
    }

    ["delete"](message) {
        this.playlist.plug.deletePlaylist(message.args[1], function(err, data) {
            if (err) {
                message.from.sendReply("Error: "+err.message);
            }
            else {
                message.from.sendReply("Playlist deleted");
            }
        });
    }

    add(message) {
        this.playlist.plug.createPlaylist(message.args[1], function(err, data) {
          if (err) {
              message.from.sendReply("Error: "+err.message);
          }
          else {
              message.from.sendReply("Playlist created, ID: "+data[0].id);
          }
      });
    }
}<|endoftext|>


<commit_before>import isPlainObject from 'lodash.isplainobject';

const modDelim = '_';
const elemDelim = '__';

/* istanbul ignore next  */
function throwError(msg, bemjson) {
    console.error(msg, bemjson);
}

export default function buildClassName(bemjson) {
    let out = '';

    if (!isPlainObject(bemjson)) {
        return out;
    }

    // block
    if (typeof bemjson.block === 'string') {
        out = bemjson.block;

        // elem
        if (typeof bemjson.elem === 'string') {
            out += elemDelim + bemjson.elem;
        }

        const entity = out;

        if (isPlainObject(bemjson.mods)) {
            Object.keys(bemjson.mods).forEach(modName => {
                const modValue = bemjson.mods[modName];
                let modValueString = '';

                if (modValue !== false) {
                    // 'short' boolean mods
                    if (modValue !== true) {
                        modValueString += modDelim + modValue;
                    }

                    out += ' ' + entity + modDelim + modName + modValueString;
                }
            });
        }
    }

    if (isPlainObject(bemjson.mix) || Array.isArray(bemjson.mix)) {
        // convert object or array into array
        const mixes = [].concat(bemjson.mix);

        mixes.forEach(mix => {
            let classDelimiter = '';

            if (out.length > 0) {
                classDelimiter = ' ';
            }

            out += classDelimiter + buildClassName(mix);
        });
    }

    // validation
    // istanbul ignore next
    if (process.env.NODE_ENV !== 'production') {
        if ('block' in bemjson) {
            if (typeof bemjson.block !== 'string') {
                throwError('block should be string', bemjson);
            }
        } else {
            if ('elem' in bemjson) {
                throwError('you should provide block along with elem', bemjson);

                if (typeof bemjson.elem !== 'string') {
                    throwError('elem should be string', bemjson);
                }
            }

            if ('mods' in bemjson) {
                throwError('you should provide block along with mods', bemjson);

                if (!isPlainObject(bemjson.mods)) {
                    throwError('mods should be a plain object', bemjson);
                }
            }
        }

        if ('mix' in bemjson) {
            if (!isPlainObject(bemjson.mix) && Array.isArray(bemjson.mix)) {
                throwError('mix should be a plain object or array on plain objects', bemjson);
            }
        }
    }

    return out;
}
<commit_msg>:see_no_evil: fix error handling for `mix`<commit_after>import isPlainObject from 'lodash.isplainobject';

const modDelim = '_';
const elemDelim = '__';

/* istanbul ignore next  */
function throwError(msg, bemjson) {
    console.error(msg, bemjson);
}

export default function buildClassName(bemjson) {
    let out = '';

    if (!isPlainObject(bemjson)) {
        return out;
    }

    // block
    if (typeof bemjson.block === 'string') {
        out = bemjson.block;

        // elem
        if (typeof bemjson.elem === 'string') {
            out += elemDelim + bemjson.elem;
        }

        const entity = out;

        if (isPlainObject(bemjson.mods)) {
            Object.keys(bemjson.mods).forEach(modName => {
                const modValue = bemjson.mods[modName];
                let modValueString = '';

                if (modValue !== false) {
                    // 'short' boolean mods
                    if (modValue !== true) {
                        modValueString += modDelim + modValue;
                    }

                    out += ' ' + entity + modDelim + modName + modValueString;
                }
            });
        }
    }

    if (isPlainObject(bemjson.mix) || Array.isArray(bemjson.mix)) {
        // convert object or array into array
        const mixes = [].concat(bemjson.mix);

        mixes.forEach(mix => {
            let classDelimiter = '';

            if (out.length > 0) {
                classDelimiter = ' ';
            }

            out += classDelimiter + buildClassName(mix);
        });
    }

    // validation
    // istanbul ignore next
    if (process.env.NODE_ENV !== 'production') {
        if ('block' in bemjson) {
            if (typeof bemjson.block !== 'string') {
                throwError('block should be string', bemjson);
            }
        } else {
            if ('elem' in bemjson) {
                throwError('you should provide block along with elem', bemjson);

                if (typeof bemjson.elem !== 'string') {
                    throwError('elem should be string', bemjson);
                }
            }

            if ('mods' in bemjson) {
                throwError('you should provide block along with mods', bemjson);

                if (!isPlainObject(bemjson.mods)) {
                    throwError('mods should be a plain object', bemjson);
                }
            }
        }

        if ('mix' in bemjson) {
            if (!isPlainObject(bemjson.mix) && !Array.isArray(bemjson.mix)) {
                throwError('mix should be a plain object or array on plain objects', bemjson);
            }
        }
    }

    return out;
}
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Mon Dec 14 2015 12:27:49 GMT+0100 (CET)

module.exports = function (config) {

    var ASSET_PATH = 'grails-app/assets';
    var TEST_PATH = 'src/test/js';

    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '../../../',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],

        // list of files / patterns to load in the browser
        files: [
            { pattern: 'target/plugins/jquery-*/web-app/js/jquery/jquery-*.js', watched: false },
            { pattern: ASSET_PATH + '/javascripts/application.js', watched: true },
            { pattern: ASSET_PATH + '/javascripts/csi/defaultMappingCsvValidator.js', watched: true },
            { pattern: TEST_PATH + '/specs/**/*.js', watched: true }
        ],

        // test results reporter to use
        // possible values: 'dots', 'progress', 'remote'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_ERROR,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true,

        // Concurrency level
        // how many browser should be started simultanous
        concurrency: Infinity,

        plugins: [
            'karma-jasmine',
            'karma-phantomjs-launcher',
            'karma-remote-reporter'
        ]
    })
};<commit_msg>Fixed karma.conf to import jquery<commit_after>// Karma configuration
// Generated on Mon Dec 14 2015 12:27:49 GMT+0100 (CET)

module.exports = function (config) {

    var ASSET_PATH = 'grails-app/assets';
    var TEST_PATH = 'src/test/js';

    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '../../../',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],

        // list of files / patterns to load in the browser
        files: [
            { pattern: ASSET_PATH + '/javascripts/jquery/jquery-*.js', watched: true },
            { pattern: ASSET_PATH + '/javascripts/application.js', watched: true },
            { pattern: ASSET_PATH + '/javascripts/csi/defaultMappingCsvValidator.js', watched: true },
            { pattern: TEST_PATH + '/specs/**/*.js', watched: true }
        ],

        // test results reporter to use
        // possible values: 'dots', 'progress', 'remote'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_ERROR,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true,

        // Concurrency level
        // how many browser should be started simultanous
        concurrency: Infinity,

        plugins: [
            'karma-jasmine',
            'karma-phantomjs-launcher',
            'karma-remote-reporter'
        ]
    })
};<|endoftext|>


<commit_before>/*

Uses the Google Sheets API to pull data from Sheets and load it onto shared
state. Writes the data out to JSON for later reference. Does not currently
check for existing data to merge--it does a fresh pull every time.

Sheets must be both A) shared publicly and B) "published to web" before they
can be accessed by this task.

*/

var project = require("../project.json");
var async = require("async");
var sheets = require("google-spreadsheets");

var camelCase = function(str) {
  return str.replace(/[^\w]+(\w)/g, function(all, match) {
    return match.toUpperCase();
  });
};

module.exports = function(grunt) {

  grunt.registerTask("sheets", "Downloads from Google Sheets -> JSON", function() {

    var auth = require("../auth.json");
    var sheetKeys = project.sheets || (auth.google && auth.google.sheets);

    if (!sheetKeys || !sheetKeys.length) {
      return grunt.fail.fatal("You must specify a spreadsheet key in project.json or auth.json!");
    }

    var done = this.async();

    async.each(sheetKeys, function(key, bookDone) {
      sheets({
        key: key,
        worksheet: 1
      }, function(err, book) {
        if (err || !book) {
          grunt.fail.warn("Unable to access book for " + key + ", is it 'published' in Sheets?");
          return bookDone();
        }
        //download each worksheet
        async.each(book.worksheets, function(page, pageDone) {
          page.rows({}, function(err, rows) {
            if (err) {
              grunt.fail.warn("Couldn't load sheet for " + book.title);
              return pageDone();
            }
            //don't break on zero-length sheets
            if (rows.length === 0) return pageDone();
            //remove extraneous GApps detail
            rows.forEach(function(row) {
              delete row.updated;
              delete row.content;
              if (typeof row.id == "object") delete row.id;
              //eliminate empty cells, which get a weird placeholder object
              for (var key in row) {
                var prop = row[key];
                if (typeof prop == "object" && "$t" in prop && prop.$t === "") {
                  row[key] = "";
                }
              }
            });
            //is this a keyed sheet, instead of a numbered list?
            if ("key" in rows[0]) {
              var obj = {};
              rows.forEach(function(row) {
                obj[row.key] = row;
                //sheets adds a title for some reason
                if (row.title == row.key) delete row.title;
                delete row.key;
              });
              rows = obj;
            }
            var filename = "data/" + camelCase(book.title) + "_" + camelCase(page.title) + ".sheet.json";
            grunt.file.write(filename, JSON.stringify(rows, null, 2));
            pageDone();
          });
        }, function() {
          //when done with all sheets, continue
          bookDone();
        });
      });
    }, function() {
      return done();
    });

  });

};<commit_msg>Sheets doesn't require auth.json anymore<commit_after>/*

Uses the Google Sheets API to pull data from Sheets and load it onto shared
state. Writes the data out to JSON for later reference. Does not currently
check for existing data to merge--it does a fresh pull every time.

Sheets must be both A) shared publicly and B) "published to web" before they
can be accessed by this task.

*/

var project = require("../project.json");
var async = require("async");
var sheets = require("google-spreadsheets");

var camelCase = function(str) {
  return str.replace(/[^\w]+(\w)/g, function(all, match) {
    return match.toUpperCase();
  });
};

var cast = function(str) {
  if (typeof str !== "string") return str;
  if (str.match(/^[\d\.,]+$/)) {
    //number
    return Number(str.replace(/,/g, ""));
  }
  if (str.toLowerCase() == "true" || str.toLowerCase() == "false") {
    return str.toLowerCase() == "true" ? true : false;
  }
  return str;
};

module.exports = function(grunt) {

  grunt.registerTask("sheets", "Downloads from Google Sheets -> JSON", function() {

    var auth = {};
    try {
      auth = require("../auth.json");
    } catch (_) { /* no auth.json, that's fine */ }
    var sheetKeys = project.sheets || (auth.google && auth.google.sheets);

    if (!sheetKeys || !sheetKeys.length) {
      return grunt.fail.fatal("You must specify a spreadsheet key in project.json or auth.json!");
    }

    var done = this.async();

    async.each(sheetKeys, function(key, bookDone) {
      sheets({
        key: key,
        worksheet: 1
      }, function(err, book) {
        if (err || !book) {
          grunt.fail.warn("Unable to access book for " + key + ", is it 'published' in Sheets?");
          return bookDone();
        }
        //download each worksheet
        async.each(book.worksheets, function(page, pageDone) {
          page.rows({}, function(err, rows) {
            if (err) {
              grunt.fail.warn("Couldn't load sheet for " + book.title);
              return pageDone();
            }
            //don't break on zero-length sheets
            if (rows.length === 0) return pageDone();
            //remove extraneous GApps detail
            rows.forEach(function(row) {
              delete row.updated;
              delete row.content;
              if (typeof row.id == "object") delete row.id;
              //eliminate empty cells, which get a weird placeholder object
              for (var key in row) {
                var prop = row[key];
                if (typeof prop == "object" && "$t" in prop && prop.$t === "") {
                  row[key] = "";
                }
                row[key] = cast(prop);
              }
            });
            //is this a keyed sheet, instead of a numbered list?
            if ("key" in rows[0]) {
              var obj = {};
              rows.forEach(function(row) {
                obj[row.key] = row;
                //sheets adds a title for some reason
                if (row.title == row.key) delete row.title;
                delete row.key;
              });
              rows = obj;
            }
            var filename = "data/" + camelCase(book.title) + "_" + camelCase(page.title) + ".sheet.json";
            grunt.file.write(filename, JSON.stringify(rows, null, 2));
            pageDone();
          });
        }, function() {
          //when done with all sheets, continue
          bookDone();
        });
      });
    }, function() {
      return done();
    });

  });

};
<|endoftext|>


<commit_before>var assert = require('assert');

var makeChange = function() {
  return 1;
};

var test = function() {
  assert.equal(1, makeChange(1));
};

test();
<commit_msg>haven't defined any coins<commit_after>var assert = require('assert');

var testThereAreFourTypesOfCoins = function() {
  var coins = getCoins();
  assert.equal(coins.length, 4);
};

var test = function() {
  testThereAreFourTypesOfCoins();
};

test();
<|endoftext|>


<commit_before>const _ = require('lodash');
const util = require('../../core/util.js');
const config = util.getConfig();
const dirs = util.dirs();
const moment = require('moment');

const log = require(dirs.core + 'log');
const Broker = require(dirs.gekko + '/exchange/gekkoBroker');

const Trader = function(next) {

  _.bindAll(this);

  this.brokerConfig = {
    ...config.trader,
    ...config.watch,
    private: true
  }

  this.propogatedTrades = 0;

  this.broker = new Broker(this.brokerConfig);

  if(!this.broker.capabilities.gekkoBroker) {
    util.die('This exchange is not yet supported');
  }

  this.sync(() => {
    this.setBalance();
    log.info('\t', 'Portfolio:');
    log.info('\t\t', this.portfolio.currency, this.brokerConfig.currency);
    log.info('\t\t', this.portfolio.asset, this.brokerConfig.asset);
    log.info('\t', 'Balance:');
    log.info('\t\t', this.balance, this.brokerConfig.currency);
    log.info('\t', 'Exposed:');
    log.info('\t\t',
      this.exposed ? 'yes' : 'no',
      `(${(this.exposure * 100).toFixed(2)}%)`
    );
    next();
  });

  this.cancellingOrder = false;
  this.sendInitialPortfolio = false;

  setInterval(this.sync, 1000 * 60 * 10);
}

// teach our trader events
util.makeEventEmitter(Trader);

Trader.prototype.sync = function(next) {
  log.debug('syncing private data');
  this.broker.syncPrivateData(() => {
    if(!this.price) {
      this.price = this.broker.ticker.bid;
    }

    const oldPortfolio = this.portfolio;

    this.setPortfolio();

    if(this.sendInitialPortfolio && !_.isEqual(oldPortfolio, this.portfolio)) {
      this.relayPortfolioChange();
    }

    // balance is relayed every minute
    // no need to do it here.

    if(next) {
      next();
    }
  });
}

Trader.prototype.relayPortfolioChange = function() {
  this.deferredEmit('portfolioChange', {
    asset: this.portfolio.asset,
    currency: this.portfolio.currency
  });
}

Trader.prototype.relayPortfolioValueChange = function() {
  this.deferredEmit('portfolioValueChange', {
    balance: this.balance
  });
}

Trader.prototype.setPortfolio = function() {
  this.portfolio = {
    currency: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.currency
    ).amount,
    asset: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.asset
    ).amount
  }
}

Trader.prototype.setBalance = function() {
  this.balance = this.portfolio.currency + this.portfolio.asset * this.price;
  this.exposure = (this.portfolio.asset * this.price) / this.balance;
  // if more than 10% of balance is in asset we are exposed
  this.exposed = this.exposure > 0.1;
}

Trader.prototype.processCandle = function(candle, done) {
  this.price = candle.close;
  const previousBalance = this.balance;
  this.setPortfolio();
  this.setBalance();

  if(!this.sendInitialPortfolio) {
    this.sendInitialPortfolio = true;
    this.deferredEmit('portfolioChange', {
      asset: this.portfolio.asset,
      currency: this.portfolio.currency
    });
  }

  if(this.balance !== previousBalance) {
    // this can happen because:
    // A) the price moved and we have > 0 asset
    // B) portfolio got changed
    this.relayPortfolioValueChange();
  }

  done();
}

Trader.prototype.processAdvice = function(advice) {
  let direction;

  if(advice.recommendation === 'long') {
    direction = 'buy';
  } else if(advice.recommendation === 'short') {
    direction = 'sell';
  } else {
    log.error('ignoring advice in unknown direction');
    return;
  }

  const id = 'trade-' + (++this.propogatedTrades);

  if(this.order) {
    if(this.order.side === direction) {
      return log.info('ignoring advice: already in the process to', direction);
    }

    if(this.cancellingOrder) {
      return log.info('ignoring advice: already cancelling previous', this.order.side, 'order');
    }

    log.info('Received advice to', direction, 'however Gekko is already in the process to', this.order.side);
    log.info('Canceling', this.order.side, 'order first');
    return this.cancelOrder(id, advice, () => this.processAdvice(advice));
  }

  let amount;

  if(direction === 'buy') {

    if(this.exposed) {
      log.info('NOT buying, already exposed');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.currency / this.price * 0.95;

    log.info(
      'Trader',
      'Received advice to go long.',
      'Buying ', this.brokerConfig.asset
    );

  } else if(direction === 'sell') {

    if(!this.exposed) {
      log.info('NOT selling, already no exposure');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.asset * 0.95;

    log.info(
      'Trader',
      'Received advice to go short.',
      'Selling ', this.brokerConfig.asset
    );
  }

  this.createOrder(direction, amount, advice, id);
}

Trader.prototype.createOrder = function(side, amount, advice, id) {
  const type = 'sticky';

  // NOTE: this is the best check we can do at this point
  // with the best price we have. The order won't be actually
  // created with this.price, but it should be close enough to
  // catch non standard errors (lot size, price filter) on
  // exchanges that have them.
  const check = this.broker.isValidOrder(amount, this.price);

  if(!check.valid) {
    log.debug('NOT creating order! Reason:', check.reason);
    return this.deferredEmit('tradeAborted', {
      id,
      adviceId: advice.id,
      action: side,
      portfolio: this.portfolio,
      balance: this.balance,
      reason: check.reason
    });
  }

  log.debug('Creating order to', side, amount, this.brokerConfig.asset);

  this.deferredEmit('tradeInitiated', {
    id,
    adviceId: advice.id,
    action: side,
    portfolio: this.portfolio,
    balance: this.balance
  });

  this.order = this.broker.createOrder(type, side, amount);

  this.order.on('filled', f => log.info('[ORDER] partial', side, ' fill, total filled:', f));
  this.order.on('statusChange', s => log.debug('[ORDER] statusChange:', s));

  this.order.on('error', e => {
    log.error('[ORDER] Gekko received error from GB:', e.message);
    log.debug(e);
    this.order = null;
    this.cancellingOrder = false;

    this.deferredEmit('tradeErrored', {
      id,
      adviceId: advice.id,
      date: moment(),
      reason: e.message
    });

  });
  this.order.on('completed', () => {
    this.order.createSummary((err, summary) => {
      log.info('[ORDER] summary:', summary);
      this.order = null;
      this.sync(() => {

        let cost;
        if(_.isNumber(summary.feePercent)) {
          cost = summary.feePercent / 100 * summary.amount * summary.price;
        }

        let effectivePrice;
        if(_.isNumber(summary.feePercent)) {
          if(side === 'buy') {
            effectivePrice = summary.price * (1 + summary.feePercent / 100);
          } else {
            effectivePrice = summary.price * (1 - summary.feePercent / 100);
          }
        } else {
          log.debug('WARNING: exchange did not provide fee information, assuming no fees..');
          effectivePrice = summary.price;
        }

        this.deferredEmit('tradeCompleted', {
          id,
          adviceId: advice.id,
          action: summary.side,
          cost,
          amount: summary.amount,
          price: summary.price,
          portfolio: this.portfolio,
          balance: this.balance,
          date: summary.date,
          feePercent: summary.feePercent,
          effectivePrice
        });
      });
    })
  });
}

Trader.prototype.cancelOrder = function(id, advice, next) {

  if(!this.order) {
    return next();
  }

  this.cancellingOrder = true;

  this.order.removeAllListeners();
  this.order.cancel();
  this.order.once('completed', () => {
    this.order = null;
    this.cancellingOrder = false;
    this.deferredEmit('tradeCancelled', {
      id,
      adviceId: advice.id,
      date: moment()
    });
    this.sync(next);
  });
}

module.exports = Trader;
<commit_msg>Changed several log messages from debug to warn (#2374)<commit_after>const _ = require('lodash');
const util = require('../../core/util.js');
const config = util.getConfig();
const dirs = util.dirs();
const moment = require('moment');

const log = require(dirs.core + 'log');
const Broker = require(dirs.gekko + '/exchange/gekkoBroker');

const Trader = function(next) {

  _.bindAll(this);

  this.brokerConfig = {
    ...config.trader,
    ...config.watch,
    private: true
  }

  this.propogatedTrades = 0;

  this.broker = new Broker(this.brokerConfig);

  if(!this.broker.capabilities.gekkoBroker) {
    util.die('This exchange is not yet supported');
  }

  this.sync(() => {
    this.setBalance();
    log.info('\t', 'Portfolio:');
    log.info('\t\t', this.portfolio.currency, this.brokerConfig.currency);
    log.info('\t\t', this.portfolio.asset, this.brokerConfig.asset);
    log.info('\t', 'Balance:');
    log.info('\t\t', this.balance, this.brokerConfig.currency);
    log.info('\t', 'Exposed:');
    log.info('\t\t',
      this.exposed ? 'yes' : 'no',
      `(${(this.exposure * 100).toFixed(2)}%)`
    );
    next();
  });

  this.cancellingOrder = false;
  this.sendInitialPortfolio = false;

  setInterval(this.sync, 1000 * 60 * 10);
}

// teach our trader events
util.makeEventEmitter(Trader);

Trader.prototype.sync = function(next) {
  log.debug('syncing private data');
  this.broker.syncPrivateData(() => {
    if(!this.price) {
      this.price = this.broker.ticker.bid;
    }

    const oldPortfolio = this.portfolio;

    this.setPortfolio();

    if(this.sendInitialPortfolio && !_.isEqual(oldPortfolio, this.portfolio)) {
      this.relayPortfolioChange();
    }

    // balance is relayed every minute
    // no need to do it here.

    if(next) {
      next();
    }
  });
}

Trader.prototype.relayPortfolioChange = function() {
  this.deferredEmit('portfolioChange', {
    asset: this.portfolio.asset,
    currency: this.portfolio.currency
  });
}

Trader.prototype.relayPortfolioValueChange = function() {
  this.deferredEmit('portfolioValueChange', {
    balance: this.balance
  });
}

Trader.prototype.setPortfolio = function() {
  this.portfolio = {
    currency: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.currency
    ).amount,
    asset: _.find(
      this.broker.portfolio.balances,
      b => b.name === this.brokerConfig.asset
    ).amount
  }
}

Trader.prototype.setBalance = function() {
  this.balance = this.portfolio.currency + this.portfolio.asset * this.price;
  this.exposure = (this.portfolio.asset * this.price) / this.balance;
  // if more than 10% of balance is in asset we are exposed
  this.exposed = this.exposure > 0.1;
}

Trader.prototype.processCandle = function(candle, done) {
  this.price = candle.close;
  const previousBalance = this.balance;
  this.setPortfolio();
  this.setBalance();

  if(!this.sendInitialPortfolio) {
    this.sendInitialPortfolio = true;
    this.deferredEmit('portfolioChange', {
      asset: this.portfolio.asset,
      currency: this.portfolio.currency
    });
  }

  if(this.balance !== previousBalance) {
    // this can happen because:
    // A) the price moved and we have > 0 asset
    // B) portfolio got changed
    this.relayPortfolioValueChange();
  }

  done();
}

Trader.prototype.processAdvice = function(advice) {
  let direction;

  if(advice.recommendation === 'long') {
    direction = 'buy';
  } else if(advice.recommendation === 'short') {
    direction = 'sell';
  } else {
    log.error('ignoring advice in unknown direction');
    return;
  }

  const id = 'trade-' + (++this.propogatedTrades);

  if(this.order) {
    if(this.order.side === direction) {
      return log.info('ignoring advice: already in the process to', direction);
    }

    if(this.cancellingOrder) {
      return log.info('ignoring advice: already cancelling previous', this.order.side, 'order');
    }

    log.info('Received advice to', direction, 'however Gekko is already in the process to', this.order.side);
    log.info('Canceling', this.order.side, 'order first');
    return this.cancelOrder(id, advice, () => this.processAdvice(advice));
  }

  let amount;

  if(direction === 'buy') {

    if(this.exposed) {
      log.info('NOT buying, already exposed');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.currency / this.price * 0.95;

    log.info(
      'Trader',
      'Received advice to go long.',
      'Buying ', this.brokerConfig.asset
    );

  } else if(direction === 'sell') {

    if(!this.exposed) {
      log.info('NOT selling, already no exposure');
      return this.deferredEmit('tradeAborted', {
        id,
        adviceId: advice.id,
        action: direction,
        portfolio: this.portfolio,
        balance: this.balance,
        reason: "Portfolio already in position."
      });
    }

    amount = this.portfolio.asset * 0.95;

    log.info(
      'Trader',
      'Received advice to go short.',
      'Selling ', this.brokerConfig.asset
    );
  }

  this.createOrder(direction, amount, advice, id);
}

Trader.prototype.createOrder = function(side, amount, advice, id) {
  const type = 'sticky';

  // NOTE: this is the best check we can do at this point
  // with the best price we have. The order won't be actually
  // created with this.price, but it should be close enough to
  // catch non standard errors (lot size, price filter) on
  // exchanges that have them.
  const check = this.broker.isValidOrder(amount, this.price);

  if(!check.valid) {
    log.warn('NOT creating order! Reason:', check.reason);
    return this.deferredEmit('tradeAborted', {
      id,
      adviceId: advice.id,
      action: side,
      portfolio: this.portfolio,
      balance: this.balance,
      reason: check.reason
    });
  }

  log.debug('Creating order to', side, amount, this.brokerConfig.asset);

  this.deferredEmit('tradeInitiated', {
    id,
    adviceId: advice.id,
    action: side,
    portfolio: this.portfolio,
    balance: this.balance
  });

  this.order = this.broker.createOrder(type, side, amount);

  this.order.on('filled', f => log.info('[ORDER] partial', side, ' fill, total filled:', f));
  this.order.on('statusChange', s => log.debug('[ORDER] statusChange:', s));

  this.order.on('error', e => {
    log.error('[ORDER] Gekko received error from GB:', e.message);
    log.debug(e);
    this.order = null;
    this.cancellingOrder = false;

    this.deferredEmit('tradeErrored', {
      id,
      adviceId: advice.id,
      date: moment(),
      reason: e.message
    });

  });
  this.order.on('completed', () => {
    this.order.createSummary((err, summary) => {
      log.info('[ORDER] summary:', summary);
      this.order = null;
      this.sync(() => {

        let cost;
        if(_.isNumber(summary.feePercent)) {
          cost = summary.feePercent / 100 * summary.amount * summary.price;
        }

        let effectivePrice;
        if(_.isNumber(summary.feePercent)) {
          if(side === 'buy') {
            effectivePrice = summary.price * (1 + summary.feePercent / 100);
          } else {
            effectivePrice = summary.price * (1 - summary.feePercent / 100);
          }
        } else {
          log.warn('WARNING: exchange did not provide fee information, assuming no fees..');
          effectivePrice = summary.price;
        }

        this.deferredEmit('tradeCompleted', {
          id,
          adviceId: advice.id,
          action: summary.side,
          cost,
          amount: summary.amount,
          price: summary.price,
          portfolio: this.portfolio,
          balance: this.balance,
          date: summary.date,
          feePercent: summary.feePercent,
          effectivePrice
        });
      });
    })
  });
}

Trader.prototype.cancelOrder = function(id, advice, next) {

  if(!this.order) {
    return next();
  }

  this.cancellingOrder = true;

  this.order.removeAllListeners();
  this.order.cancel();
  this.order.once('completed', () => {
    this.order = null;
    this.cancellingOrder = false;
    this.deferredEmit('tradeCancelled', {
      id,
      adviceId: advice.id,
      date: moment()
    });
    this.sync(next);
  });
}

module.exports = Trader;
<|endoftext|>


<commit_before><commit_msg>Removing IE9 from the tests because it is not a supported browser<commit_after><|endoftext|>