<commit_before>/// <reference path="../lib/jquery-2.1.1.min.js" />
/// <reference path="../lib/qunit-git.js" />
/// <reference path="../project/zquery.js" />

// TODO: Remove helpers.js
// TODO: Split Modules into separate JS files

window.$Q = window.QUnit;

<commit_msg>Updated TODO<commit_after>/// <reference path="../lib/jquery-2.1.1.min.js" />
/// <reference path="../lib/qunit-git.js" />
/// <reference path="../project/zquery.js" />

// TODO: Remove helpers.js

window.$Q = window.QUnit;

<|endoftext|>


<commit_before>/* jshint onevar:false */

'use strict';

var async = require('async');
var opn = require('opn');
var gen = require('yeoman-generator');
var yosay = require('yosay');
var util = require('util');
var path = require('path');
var updateNotifier = require('update-notifier');
var chalk = require('chalk');
var findup = require('findup');
var fullname = require('fullname');
var _s = require('underscore.string');
var Configstore = require('configstore');
var pkg = require('./package.json');

var conf = new Configstore(pkg.name, {
  generatorRunCount: {}
});

function namespaceToName(val) {
  return val.replace(/(\w+):\w+/, '$1');
}

// The `yo yo` generator provides users with a few common, helpful commands.
var yoyo = module.exports = function yoyo(args, options) {
  gen.Base.apply(this, arguments);
  this.insight = options.insight;

  this.insight.track('yoyo', 'init');
  process.once('exit', this._exit.bind(this));
};

util.inherits(yoyo, gen.Base);

// Runs parallel `npm update -g`s for each selected generator.
yoyo.prototype._updateGenerators = function _updateGenerators(pkgs) {
  var self = this;

  var resolveGenerators = function (pkg) {
    return function (next) {
      self.spawnCommand('npm', ['update', '-g', pkg])
        .on('error', next)
        .on('exit', next);
    };
  };

  self.insight.track('yoyo', 'update');
  async.parallel(self._.map(pkgs, resolveGenerators), function (err) {
    if (err) {
      self.insight.track('yoyo:err', 'update');
      return self.emit('error', err);
    }

    self.insight.track('yoyo', 'updated');
    self.home({
      refresh: true,
      message:
        'I\'ve just updated your generators. Remember, you can update' +
        '\na specific generator with npm by running:\n' +
        chalk.magenta('\n    npm update -g generator-_______')
    });
  });
};

// Prompts the user to select which generators to update
yoyo.prototype._promptToUpdateGenerators = function _promptToUpdateGenerators() {
  this.prompt([{
    name: '_updateSelectedGenerators',
    message: 'Generators to update',
    type: 'checkbox',
    choices: this._.map(this.pkgs, function (generator) {
      return {name: generator.name, checked: true};
    })
  }], function (answer) {
    this._updateGenerators.call(this, answer._updateSelectedGenerators);
  }.bind(this));
};

// Initializes a generator.
//
// - generator - (string) The generator to initialize.
yoyo.prototype._initGenerator = function _initGenerator(generator, done) {
  console.log(
    chalk.yellow('\nMake sure you are in the directory you want to scaffold into.\n') +
    chalk.dim('This generator can also be run with: ' +
    chalk.blue('yo ' + generator.split(':')[0]) + '\n')
  );

  // save the generator run count
  var generatorName = namespaceToName(generator);
  var generatorRunCount = conf.get('generatorRunCount');
  generatorRunCount[generatorName] = typeof generatorRunCount[generatorName] === 'number' ?
    ++generatorRunCount[generatorName] : 1;
  conf.set('generatorRunCount', generatorRunCount);

  this.insight.track('yoyo', 'run', generator);
  this.composeWith(generator);
  done();
};

// Serves as the response prompt for "Install a generator" as well as simply
// installs a generator if a string is passed in.
//
// - pkgName - (optional) A string that matches the NPM package name.
yoyo.prototype._installGenerator = function _installGenerator(pkgName) {
  if (this._.isString(pkgName)) {
    this.insight.track('yoyo', 'install', pkgName);

    // We know what generator we want to install
    return this.spawnCommand('npm', ['install', '-g', pkgName])
      .on('error', function (err) {
        this.insight.track('yoyo:err', 'install', pkgName);
        this.emit('error', err);
      }.bind(this))
      .on('exit', function () {
        this.insight.track('yoyo', 'installed', pkgName);
        this.home({
          refresh: true,
          message:
            '\nI just installed a generator by running:\n' +
            chalk.blue.bold('\n    npm install -g ' + pkgName)
        });
      }.bind(this));
  }

  this.insight.track('yoyo', 'install');

  this.prompt([{
    name: 'searchTerm',
    message: 'Search NPM for generators'
  }], this._searchNpm.bind(this));
};

// Grabs all of the packages with a `yeoman-generator` keyword on NPM.
//
// - term - (object) Contains the search term & gets passed back to callback().
// - cb   - Callback to execute once generators have been found.
yoyo.prototype._findAllNpmGenerators = function _findAllNpmGenerators(term, cb) {
  var url = 'http://isaacs.iriscouch.com/registry/_design/' +
    'app/_view/byKeyword?startkey=[%22yeoman-generator%22]' +
    '&endkey=[%22yeoman-generator%22,{}]&group_level=3';

  this.request(url, function (err, res, body) {
    if (err) {
      return this.emit('error', err);
    }

    try {
      this.npmGenerators = JSON.parse(body);
    } catch (err) {
      return this.emit('error', new Error(chalk.bold(
        'A problem occurred contacting the registry.' +
        '\nUnable to parse response: not valid JSON.'
      )));
    }

    cb(term);
  }.bind(this));
};

// Takes a search term, looks it up in the registry, prompts the user with the
// results, allowing them to choose to install it, or go back home.
//
// - term - Object with a 'searchTerm' property containing the term to search
//          NPM for.
yoyo.prototype._searchNpm = function _searchNpm(term) {
  if (!this.npmGenerators) {
    return this._findAllNpmGenerators(term, this._searchNpm.bind(this));
  }

  // Find any matches from NPM.
  var choices = this._.chain(this.npmGenerators.rows).map(function (generator) {
    // Make sure it's not already installed.
    if (
      !this.pkgs[generator.key[1]] &&
      generator.key.join(' ').indexOf(term.searchTerm) > -1
    ) {
      return {
        name: generator.key[1],
        value: generator.key[1]
      };
    }
  }.bind(this)).compact().value();

  var resultsPrompt = [{
    name: '_installGenerator',
    type: 'list',
    message: choices.length > 0 ?
      'Here\'s what I found. Install one?' : 'Sorry, nothing was found',
    choices: this._.union(choices, [{
      name: 'Search again',
      value: '_installGenerator'
    }, {
      name: 'Return home',
      value: 'home'
    }])
  }];

  this.prompt(resultsPrompt, function (answer) {
    if (this._.isFunction(this[answer._installGenerator])) {
      return this[answer._installGenerator]();
    }

    this._installGenerator(answer._installGenerator);
  }.bind(this));
};

// Prompts user with a few helpful resources, then opens it in their browser.
yoyo.prototype._findHelp = function _findHelp() {
  this.insight.track('yoyo', 'help');
  this.prompt([{
    name: 'whereTo',
    type: 'list',
    message:
      'Here are a few helpful resources.\n' +
      '\nI will open the link you select in your browser for you',
    choices: [{
      name: 'Take me to the documentation',
      value: 'http://yeoman.io/learning/index.html'
    }, {
      name: 'View Frequently Asked Questions',
      value: 'http://yeoman.io/learning/faq.html'
    }, {
      name: 'File an issue on GitHub',
      value: 'http://yeoman.io/contributing/opening-issues.html'
    }, {
      name: 'Take me back home, Yo!',
      value: {
        method: 'home',
        args: {
          message: 'I get it, you like learning on your own. I respect that.'
        }
      }
    }]
  }], function (answer) {
    this.insight.track('yoyo', 'help', answer);

    if (this._.isFunction(this[answer.whereTo.method])) {
      this[answer.whereTo.method](answer.whereTo.args);
    } else {
      opn(answer.whereTo);
    }
  }.bind(this));
};

// Serves as a quick escape from the `yo yo` prompts.
yoyo.prototype._exit = function _exit() {
  this.insight.track('yoyo', 'exit');

  var url = 'https://github.com/yeoman/yeoman#team';
  var maxLength = url.length;
  var newLine = new Array(maxLength).join(' ');

  console.log(
    '\n' +
    yosay(
      'Bye from us! Chat soon.' +
      newLine +
      newLine +
      'The Yeoman Team ' + url,
      { maxLength: maxLength }
    )
  );
};

// I'm sorry...
yoyo.prototype._noop = function _noop() {};

// Rolls through all of the generators provided by `env.generators`, finding
// their `package.json` files, then storing them internally in `this.pkgs`.
yoyo.prototype.findGenerators = function findGenerators() {
  this.pkgs = {};

  var resolveGenerators = function (generator) {
    if (!/(app|all)$/.test(generator.namespace)) {
      return;
    }

    var dir = findup.sync(generator.resolved, 'package.json');
    if (!dir) {
      return;
    }

    var pkg = gen.file.readJSON(path.join(dir, 'package.json'));
    pkg.namespace = generator.namespace;
    pkg.appGenerator = true;
    pkg.prettyName = _s.titleize(_s.humanize(namespaceToName(generator.namespace)));

    pkg.update = updateNotifier({
      packageName: pkg.name,
      packageVersion: pkg.version
    }).update;

    if (pkg.update && pkg.version !== pkg.update.latest) {
      pkg.updateAvailable = true;
    }

    this.pkgs[pkg.name] = pkg;
  };

  this._.each(this.env.getGeneratorsMeta(), resolveGenerators, this);
};

// Display the home `yo` screen, with the intial set of options.
//
// - options - (optional)
//           - message (string) - String to print before prompt.
//           - refresh (bool) - Spawn a new `yo` command.
yoyo.prototype.home = function home(options) {
  var done = this.async();

  options = options || {};

  if (options.refresh) {
    this.env.lookup();
    this.findGenerators();
  }

  if (options.message) {
    console.log('\n' + chalk.cyan(options.message) + '\n');
  }

  var defaultChoices = [{
    name: 'Install a generator',
    value: {
      method: '_installGenerator'
    }
  }, {
    name: 'Find some help',
    value: {
      method: '_findHelp'
    }
  }, {
    name: 'Get me out of here!',
    value: {
      method: '_noop'
    }
  }];

  var generatorList = this._.chain(this.pkgs).map(function (generator) {
    if (!generator.appGenerator) {
      return;
    }

    var updateInfo = generator.updateAvailable ? chalk.dim.yellow(' ♥ Update Available!') : '';

    // TODO
    // var updateInfo = generator.updateAvailable ?
    //   chalk.dim.yellow(' ♥ Update Available!') +
    //   chalk.dim.reset(' (Press Space to update)') : '';

    return {
      name: generator.prettyName + updateInfo,
      value: {
        method: '_initGenerator',
        args: generator.namespace
      }
    };
  }).compact().sortBy(function (el) {
    var generatorName = namespaceToName(el.value.args);
    return -conf.get('generatorRunCount')[generatorName] || 0;
  }).value();

  if (generatorList.length) {
    defaultChoices.unshift({
      name: 'Update your generators',
      value: {
        method: '_promptToUpdateGenerators'
      }
    });
  }

  this.insight.track('yoyo', 'home');

  fullname(function (err, name) {
    if (err) {
      return done(err);
    }

    var allo = name ? ('\'Allo ' + name.split(' ')[0] + '! ') : '\'Allo! ';

    this.prompt([{
      name: 'whatNext',
      type: 'list',
      message: allo + 'What would you like to do?',
      choices: this._.flatten([
        new gen.inquirer.Separator('Run a generator'),
        generatorList,
        new gen.inquirer.Separator(),
        defaultChoices,
        new gen.inquirer.Separator()
      ])
    }], function (answer) {
      this[answer.whatNext.method](answer.whatNext.args, done);
    }.bind(this));
  }.bind(this));
};
<commit_msg>remove useless `generator-` prefix when searching for generators<commit_after>/* jshint onevar:false */

'use strict';

var async = require('async');
var opn = require('opn');
var gen = require('yeoman-generator');
var yosay = require('yosay');
var util = require('util');
var path = require('path');
var updateNotifier = require('update-notifier');
var chalk = require('chalk');
var findup = require('findup');
var fullname = require('fullname');
var _s = require('underscore.string');
var Configstore = require('configstore');
var pkg = require('./package.json');

var conf = new Configstore(pkg.name, {
  generatorRunCount: {}
});

function namespaceToName(val) {
  return val.replace(/(\w+):\w+/, '$1');
}

// The `yo yo` generator provides users with a few common, helpful commands.
var yoyo = module.exports = function yoyo(args, options) {
  gen.Base.apply(this, arguments);
  this.insight = options.insight;

  this.insight.track('yoyo', 'init');
  process.once('exit', this._exit.bind(this));
};

util.inherits(yoyo, gen.Base);

// Runs parallel `npm update -g`s for each selected generator.
yoyo.prototype._updateGenerators = function _updateGenerators(pkgs) {
  var self = this;

  var resolveGenerators = function (pkg) {
    return function (next) {
      self.spawnCommand('npm', ['update', '-g', pkg])
        .on('error', next)
        .on('exit', next);
    };
  };

  self.insight.track('yoyo', 'update');
  async.parallel(self._.map(pkgs, resolveGenerators), function (err) {
    if (err) {
      self.insight.track('yoyo:err', 'update');
      return self.emit('error', err);
    }

    self.insight.track('yoyo', 'updated');
    self.home({
      refresh: true,
      message:
        'I\'ve just updated your generators. Remember, you can update' +
        '\na specific generator with npm by running:\n' +
        chalk.magenta('\n    npm update -g generator-_______')
    });
  });
};

// Prompts the user to select which generators to update
yoyo.prototype._promptToUpdateGenerators = function _promptToUpdateGenerators() {
  this.prompt([{
    name: '_updateSelectedGenerators',
    message: 'Generators to update',
    type: 'checkbox',
    choices: this._.map(this.pkgs, function (generator) {
      return {name: generator.name, checked: true};
    })
  }], function (answer) {
    this._updateGenerators.call(this, answer._updateSelectedGenerators);
  }.bind(this));
};

// Initializes a generator.
//
// - generator - (string) The generator to initialize.
yoyo.prototype._initGenerator = function _initGenerator(generator, done) {
  console.log(
    chalk.yellow('\nMake sure you are in the directory you want to scaffold into.\n') +
    chalk.dim('This generator can also be run with: ' +
    chalk.blue('yo ' + generator.split(':')[0]) + '\n')
  );

  // save the generator run count
  var generatorName = namespaceToName(generator);
  var generatorRunCount = conf.get('generatorRunCount');
  generatorRunCount[generatorName] = typeof generatorRunCount[generatorName] === 'number' ?
    ++generatorRunCount[generatorName] : 1;
  conf.set('generatorRunCount', generatorRunCount);

  this.insight.track('yoyo', 'run', generator);
  this.composeWith(generator);
  done();
};

// Serves as the response prompt for "Install a generator" as well as simply
// installs a generator if a string is passed in.
//
// - pkgName - (optional) A string that matches the NPM package name.
yoyo.prototype._installGenerator = function _installGenerator(pkgName) {
  if (this._.isString(pkgName)) {
    this.insight.track('yoyo', 'install', pkgName);

    // We know what generator we want to install
    return this.spawnCommand('npm', ['install', '-g', pkgName])
      .on('error', function (err) {
        this.insight.track('yoyo:err', 'install', pkgName);
        this.emit('error', err);
      }.bind(this))
      .on('exit', function () {
        this.insight.track('yoyo', 'installed', pkgName);
        this.home({
          refresh: true,
          message:
            '\nI just installed a generator by running:\n' +
            chalk.blue.bold('\n    npm install -g ' + pkgName)
        });
      }.bind(this));
  }

  this.insight.track('yoyo', 'install');

  this.prompt([{
    name: 'searchTerm',
    message: 'Search NPM for generators'
  }], this._searchNpm.bind(this));
};

// Grabs all of the packages with a `yeoman-generator` keyword on NPM.
//
// - term - (object) Contains the search term & gets passed back to callback().
// - cb   - Callback to execute once generators have been found.
yoyo.prototype._findAllNpmGenerators = function _findAllNpmGenerators(term, cb) {
  var url = 'http://isaacs.iriscouch.com/registry/_design/' +
    'app/_view/byKeyword?startkey=[%22yeoman-generator%22]' +
    '&endkey=[%22yeoman-generator%22,{}]&group_level=3';

  this.request(url, function (err, res, body) {
    if (err) {
      return this.emit('error', err);
    }

    try {
      this.npmGenerators = JSON.parse(body);
    } catch (err) {
      return this.emit('error', new Error(chalk.bold(
        'A problem occurred contacting the registry.' +
        '\nUnable to parse response: not valid JSON.'
      )));
    }

    cb(term);
  }.bind(this));
};

// Takes a search term, looks it up in the registry, prompts the user with the
// results, allowing them to choose to install it, or go back home.
//
// - term - Object with a 'searchTerm' property containing the term to search
//          NPM for.
yoyo.prototype._searchNpm = function _searchNpm(term) {
  if (!this.npmGenerators) {
    return this._findAllNpmGenerators(term, this._searchNpm.bind(this));
  }

  // Find any matches from NPM.
  var choices = this._.chain(this.npmGenerators.rows).map(function (generator) {
    // Make sure it's not already installed.
    if (
      !this.pkgs[generator.key[1]] &&
      generator.key.join(' ').indexOf(term.searchTerm) > -1
    ) {
      return {
        name: generator.key[1].replace(/^generator-/, ''),
        value: generator.key[1]
      };
    }
  }.bind(this)).compact().value();

  var resultsPrompt = [{
    name: '_installGenerator',
    type: 'list',
    message: choices.length > 0 ?
      'Here\'s what I found. Install one?' : 'Sorry, nothing was found',
    choices: this._.union(choices, [{
      name: 'Search again',
      value: '_installGenerator'
    }, {
      name: 'Return home',
      value: 'home'
    }])
  }];

  this.prompt(resultsPrompt, function (answer) {
    if (this._.isFunction(this[answer._installGenerator])) {
      return this[answer._installGenerator]();
    }

    this._installGenerator(answer._installGenerator);
  }.bind(this));
};

// Prompts user with a few helpful resources, then opens it in their browser.
yoyo.prototype._findHelp = function _findHelp() {
  this.insight.track('yoyo', 'help');
  this.prompt([{
    name: 'whereTo',
    type: 'list',
    message:
      'Here are a few helpful resources.\n' +
      '\nI will open the link you select in your browser for you',
    choices: [{
      name: 'Take me to the documentation',
      value: 'http://yeoman.io/learning/index.html'
    }, {
      name: 'View Frequently Asked Questions',
      value: 'http://yeoman.io/learning/faq.html'
    }, {
      name: 'File an issue on GitHub',
      value: 'http://yeoman.io/contributing/opening-issues.html'
    }, {
      name: 'Take me back home, Yo!',
      value: {
        method: 'home',
        args: {
          message: 'I get it, you like learning on your own. I respect that.'
        }
      }
    }]
  }], function (answer) {
    this.insight.track('yoyo', 'help', answer);

    if (this._.isFunction(this[answer.whereTo.method])) {
      this[answer.whereTo.method](answer.whereTo.args);
    } else {
      opn(answer.whereTo);
    }
  }.bind(this));
};

// Serves as a quick escape from the `yo yo` prompts.
yoyo.prototype._exit = function _exit() {
  this.insight.track('yoyo', 'exit');

  var url = 'https://github.com/yeoman/yeoman#team';
  var maxLength = url.length;
  var newLine = new Array(maxLength).join(' ');

  console.log(
    '\n' +
    yosay(
      'Bye from us! Chat soon.' +
      newLine +
      newLine +
      'The Yeoman Team ' + url,
      { maxLength: maxLength }
    )
  );
};

// I'm sorry...
yoyo.prototype._noop = function _noop() {};

// Rolls through all of the generators provided by `env.generators`, finding
// their `package.json` files, then storing them internally in `this.pkgs`.
yoyo.prototype.findGenerators = function findGenerators() {
  this.pkgs = {};

  var resolveGenerators = function (generator) {
    if (!/(app|all)$/.test(generator.namespace)) {
      return;
    }

    var dir = findup.sync(generator.resolved, 'package.json');
    if (!dir) {
      return;
    }

    var pkg = gen.file.readJSON(path.join(dir, 'package.json'));
    pkg.namespace = generator.namespace;
    pkg.appGenerator = true;
    pkg.prettyName = _s.titleize(_s.humanize(namespaceToName(generator.namespace)));

    pkg.update = updateNotifier({
      packageName: pkg.name,
      packageVersion: pkg.version
    }).update;

    if (pkg.update && pkg.version !== pkg.update.latest) {
      pkg.updateAvailable = true;
    }

    this.pkgs[pkg.name] = pkg;
  };

  this._.each(this.env.getGeneratorsMeta(), resolveGenerators, this);
};

// Display the home `yo` screen, with the intial set of options.
//
// - options - (optional)
//           - message (string) - String to print before prompt.
//           - refresh (bool) - Spawn a new `yo` command.
yoyo.prototype.home = function home(options) {
  var done = this.async();

  options = options || {};

  if (options.refresh) {
    this.env.lookup();
    this.findGenerators();
  }

  if (options.message) {
    console.log('\n' + chalk.cyan(options.message) + '\n');
  }

  var defaultChoices = [{
    name: 'Install a generator',
    value: {
      method: '_installGenerator'
    }
  }, {
    name: 'Find some help',
    value: {
      method: '_findHelp'
    }
  }, {
    name: 'Get me out of here!',
    value: {
      method: '_noop'
    }
  }];

  var generatorList = this._.chain(this.pkgs).map(function (generator) {
    if (!generator.appGenerator) {
      return;
    }

    var updateInfo = generator.updateAvailable ? chalk.dim.yellow(' ♥ Update Available!') : '';

    // TODO
    // var updateInfo = generator.updateAvailable ?
    //   chalk.dim.yellow(' ♥ Update Available!') +
    //   chalk.dim.reset(' (Press Space to update)') : '';

    return {
      name: generator.prettyName + updateInfo,
      value: {
        method: '_initGenerator',
        args: generator.namespace
      }
    };
  }).compact().sortBy(function (el) {
    var generatorName = namespaceToName(el.value.args);
    return -conf.get('generatorRunCount')[generatorName] || 0;
  }).value();

  if (generatorList.length) {
    defaultChoices.unshift({
      name: 'Update your generators',
      value: {
        method: '_promptToUpdateGenerators'
      }
    });
  }

  this.insight.track('yoyo', 'home');

  fullname(function (err, name) {
    if (err) {
      return done(err);
    }

    var allo = name ? ('\'Allo ' + name.split(' ')[0] + '! ') : '\'Allo! ';

    this.prompt([{
      name: 'whatNext',
      type: 'list',
      message: allo + 'What would you like to do?',
      choices: this._.flatten([
        new gen.inquirer.Separator('Run a generator'),
        generatorList,
        new gen.inquirer.Separator(),
        defaultChoices,
        new gen.inquirer.Separator()
      ])
    }], function (answer) {
      this[answer.whatNext.method](answer.whatNext.args, done);
    }.bind(this));
  }.bind(this));
};
<|endoftext|>


<commit_before>/* jshint onevar:false */
'use strict';
var async = require('async');
var opn = require('opn');
var gen = require('yeoman-generator');
var yosay = require('yosay');
var util = require('util');
var path = require('path');
var updateNotifier = require('update-notifier');
var chalk = require('chalk');
var findup = require('findup');
var fullname = require('fullname');
var _s = require('underscore.string');
var Configstore = require('configstore');
var pkg = require('./package.json');
var conf = new Configstore(pkg.name, {
  generatorRunCount: {}
});

function namespaceToName(val) {
  return val.replace(/(\w+):\w+/, '$1');
}

// The `yo yo` generator provides users with a few common, helpful commands.
var yoyo = module.exports = function (args, options) {
  gen.Base.apply(this, arguments);
  this.insight = options.insight;

  this.insight.track('yoyo', 'init');
  process.once('exit', this._exit.bind(this));
};

util.inherits(yoyo, gen.Base);

// Runs parallel `npm install -g`s for each selected generator.
yoyo.prototype._updateGenerators = function (pkgs) {
  var self = this;

  var resolveGenerators = function (pkg) {
    return function (next) {
      self.spawnCommand('npm', ['install', '-g', pkg])
        .on('error', next)
        .on('exit', next);
    };
  };

  self.insight.track('yoyo', 'update');
  async.parallel(self._.map(pkgs, resolveGenerators), function (err) {
    if (err) {
      self.insight.track('yoyo:err', 'update');
      self.emit('error', err);
      return;
    }

    self.insight.track('yoyo', 'updated');
    self.home({
      refresh: true,
      message:
        'I\'ve just updated your generators. Remember, you can update' +
        '\na specific generator with npm by running:\n' +
        chalk.magenta('\n    npm install -g generator-_______')
    });
  });
};

// Prompts the user to select which generators to update
yoyo.prototype._promptToUpdateGenerators = function () {
  this.prompt([{
    name: '_updateSelectedGenerators',
    message: 'Generators to update',
    type: 'checkbox',
    choices: this._.map(this.pkgs, function (generator) {
      return {name: generator.name, checked: true};
    })
  }], function (answer) {
    this._updateGenerators.call(this, answer._updateSelectedGenerators);
  }.bind(this));
};

// Initializes a generator.
//
// - generator - (string) The generator to initialize.
yoyo.prototype._initGenerator = function (generator, done) {
  console.log(
    chalk.yellow('\nMake sure you are in the directory you want to scaffold into.\n') +
    chalk.dim('This generator can also be run with: ' +
    chalk.blue('yo ' + generator.split(':')[0]) + '\n')
  );

  // save the generator run count
  var generatorName = namespaceToName(generator);
  var generatorRunCount = conf.get('generatorRunCount');
  generatorRunCount[generatorName] = typeof generatorRunCount[generatorName] === 'number' ?
    ++generatorRunCount[generatorName] : 1;
  conf.set('generatorRunCount', generatorRunCount);

  this.insight.track('yoyo', 'run', generator);
  this.composeWith(generator);
  done();
};

// Serves as the response prompt for "Install a generator" as well as simply
// installs a generator if a string is passed in.
//
// - pkgName - (optional) A string that matches the NPM package name.
yoyo.prototype._installGenerator = function (pkgName) {
  if (this._.isString(pkgName)) {
    this.insight.track('yoyo', 'install', pkgName);

    // We know what generator we want to install
    return this.spawnCommand('npm', ['install', '-g', pkgName])
      .on('error', function (err) {
        this.insight.track('yoyo:err', 'install', pkgName);
        this.emit('error', err);
      }.bind(this))
      .on('exit', function () {
        this.insight.track('yoyo', 'installed', pkgName);
        this.home({
          refresh: true,
          message:
            '\nI just installed a generator by running:\n' +
            chalk.blue.bold('\n    npm install -g ' + pkgName)
        });
      }.bind(this));
  }

  this.insight.track('yoyo', 'install');

  this.prompt([{
    name: 'searchTerm',
    message: 'Search NPM for generators'
  }], this._searchNpm.bind(this));
};

// Grabs all of the packages with a `yeoman-generator` keyword on NPM.
//
// - term - (object) Contains the search term & gets passed back to callback().
// - cb   - Callback to execute once generators have been found.
yoyo.prototype._findAllNpmGenerators = function (term, cb) {
  var url = 'http://isaacs.iriscouch.com/registry/_design/' +
    'app/_view/byKeyword?startkey=[%22yeoman-generator%22]' +
    '&endkey=[%22yeoman-generator%22,{}]&group_level=3';

  this.request(url, function (err, res, body) {
    if (err) {
      this.emit('error', err);
      return;
    }

    try {
      this.npmGenerators = JSON.parse(body);
    } catch (err) {
      return this.emit('error', new Error(chalk.bold(
        'A problem occurred contacting the registry.' +
        '\nUnable to parse response: not valid JSON.'
      )));
    }

    cb(term);
  }.bind(this));
};

// Grab out name, and author from git repo of the generator
//
// - generator - (object) Contains npm registry json object.
// - cb   - Callback to execute once generators have been processed.
yoyo.prototype._handleRow = function(generator,cb){
  var url = 'https://skimdb.npmjs.com/registry/' + generator.key[1];
  this.request({url: url, json: true}, function (err, res, body) {
    if (!err && res.statusCode === 200) {
      var packageType = this._isYeomanPackage(body);
      cb(null, {
        name: packageType + generator.key[1].replace(/^generator-/, ''),
        value: generator.key[1],
        author: packageType ? body.author.name : ''
      });
    } else {
      cb(new Error('GitHub fetch failed\n' + err + '\n' + body));
    }
  }.bind(this));
};

// Determine if NPM package is a yeoman package
// - body - object containing github repo information
yoyo.prototype._isYeomanPackage = function(body){
  return body.author &&
  body.author.name === 'The Yeoman Team' ? ':} ' : '';
};

//Sorts the NPM Packages in alphabetical order
// - a - first compared NPM package
// - b - second compared NPM package
yoyo.prototype._sortNPMPackage = function(a,b){
      if (a.name < b.name){
         return -1;
      }
      if (a.name > b.name){
        return 1;
      }
      return 0;
};
// Takes a search term, looks it up in the registry, prompts the user with the
// results, allowing them to choose to install it, or go back home.
//
// - term - Object with a 'searchTerm' property containing the term to search
//          NPM for.
yoyo.prototype._searchNpm = function (term) {
  if (!this.npmGenerators) {
    return this._findAllNpmGenerators(term, this._searchNpm.bind(this));
  }

  var pkgs = this.pkgs;
  // Find any matches from NPM.
  var availableGenerators = this.npmGenerators.rows.filter(function(generator){
    return !pkgs[generator.key[1]] && generator.key.join(' ').indexOf(term.searchTerm) > -1;
  });

  async.map(availableGenerators, this._handleRow.bind(this), function(err, choices){
    choices.sort(this._sortNPMPackage);

    var introMessage = 'Sorry, nothing was found';

    if (choices.length > 0){
      introMessage = 'Here\'s what I found.\n' +
      ' :} represents yeoman offical generators.\n Install one?';
    }

    var resultsPrompt = [{
      name: '_installGenerator',
      type: 'list',
      message: introMessage,
      choices: this._.union(choices, [{
        name: 'Search again',
        value: '_installGenerator'
        }, {
        name: 'Return home',
        value: 'home'
      }])
    }];

    this.prompt(resultsPrompt, function (answer) {
      if (this._.isFunction(this[answer._installGenerator])) {
        return this[answer._installGenerator]();
      }
      this._installGenerator(answer._installGenerator);
    }.bind(this));
  }.bind(this));
};

// Prompts user with a few helpful resources, then opens it in their browser.
yoyo.prototype._findHelp = function () {
  this.insight.track('yoyo', 'help');
  this.prompt([{
    name: 'whereTo',
    type: 'list',
    message:
      'Here are a few helpful resources.\n' +
      '\nI will open the link you select in your browser for you',
    choices: [{
      name: 'Take me to the documentation',
      value: 'http://yeoman.io/learning/index.html'
    }, {
      name: 'View Frequently Asked Questions',
      value: 'http://yeoman.io/learning/faq.html'
    }, {
      name: 'File an issue on GitHub',
      value: 'http://yeoman.io/contributing/opening-issues.html'
    }, {
      name: 'Take me back home, Yo!',
      value: {
        method: 'home',
        args: {
          message: 'I get it, you like learning on your own. I respect that.'
        }
      }
    }]
  }], function (answer) {
    this.insight.track('yoyo', 'help', answer);

    if (this._.isFunction(this[answer.whereTo.method])) {
      this[answer.whereTo.method](answer.whereTo.args);
    } else {
      opn(answer.whereTo);
    }
  }.bind(this));
};

// Serves as a quick escape from the `yo yo` prompts.
yoyo.prototype._exit = function () {
  this.insight.track('yoyo', 'exit');

  var url = 'https://github.com/yeoman/yeoman#team';
  var maxLength = url.length;
  var newLine = new Array(maxLength).join(' ');

  console.log(
    '\n' +
    yosay(
      'Bye from us! Chat soon.' +
      newLine +
      newLine +
      'The Yeoman Team ' + url,
      { maxLength: maxLength }
    )
  );
};

// I'm sorry...
yoyo.prototype._noop = function () {};

// Rolls through all of the generators provided by `env.generators`, finding
// their `package.json` files, then storing them internally in `this.pkgs`.
yoyo.prototype.findGenerators = function () {
  this.pkgs = {};

  var resolveGenerators = function (generator) {
    if (!/(app|all)$/.test(generator.namespace)) {
      return;
    }

    var dir = findup.sync(generator.resolved, 'package.json');
    if (!dir) {
      return;
    }

    var pkg = gen.file.readJSON(path.join(dir, 'package.json'));
    pkg.namespace = generator.namespace;
    pkg.appGenerator = true;
    pkg.prettyName = _s.titleize(_s.humanize(namespaceToName(generator.namespace)));

    pkg.update = updateNotifier({
      packageName: pkg.name,
      packageVersion: pkg.version
    }).update;

    if (pkg.update && pkg.version !== pkg.update.latest) {
      pkg.updateAvailable = true;
    }

    this.pkgs[pkg.name] = pkg;
  };

  this._.each(this.env.getGeneratorsMeta(), resolveGenerators, this);
};

// Display the home `yo` screen, with the intial set of options.
//
// - options - (optional)
//           - message (string) - String to print before prompt.
//           - refresh (bool) - Spawn a new `yo` command.
yoyo.prototype.home = function (options) {
  var done = this.async();

  options = options || {};

  if (options.refresh) {
    this.env.lookup();
    this.findGenerators();
  }

  if (options.message) {
    console.log('\n' + chalk.cyan(options.message) + '\n');
  }

  var defaultChoices = [{
    name: 'Install a generator',
    value: {
      method: '_installGenerator'
    }
  }, {
    name: 'Find some help',
    value: {
      method: '_findHelp'
    }
  }, {
    name: 'Get me out of here!',
    value: {
      method: '_noop'
    }
  }];

  var generatorList = this._.chain(this.pkgs).map(function (generator) {
    if (!generator.appGenerator) {
      return;
    }

    var updateInfo = generator.updateAvailable ? chalk.dim.yellow(' ♥ Update Available!') : '';

    // TODO
    // var updateInfo = generator.updateAvailable ?
    //   chalk.dim.yellow(' ♥ Update Available!') +
    //   chalk.dim.reset(' (Press Space to update)') : '';

    return {
      name: generator.prettyName + updateInfo,
      value: {
        method: '_initGenerator',
        args: generator.namespace
      }
    };
  }).compact().sortBy(function (el) {
    var generatorName = namespaceToName(el.value.args);
    return -conf.get('generatorRunCount')[generatorName] || 0;
  }).value();

  if (generatorList.length) {
    defaultChoices.unshift({
      name: 'Update your generators',
      value: {
        method: '_promptToUpdateGenerators'
      }
    });
  }

  this.insight.track('yoyo', 'home');

  fullname(function (err, name) {
    if (err) {
      done(err);
      return;
    }

    var allo = name ? ('\'Allo ' + name.split(' ')[0] + '! ') : '\'Allo! ';

    this.prompt([{
      name: 'whatNext',
      type: 'list',
      message: allo + 'What would you like to do?',
      choices: this._.flatten([
        new gen.inquirer.Separator('Run a generator'),
        generatorList,
        new gen.inquirer.Separator(),
        defaultChoices,
        new gen.inquirer.Separator()
      ])
    }], function (answer) {
      this[answer.whatNext.method](answer.whatNext.args, done);
    }.bind(this));
  }.bind(this));
};
<commit_msg>Add color to search official generators token<commit_after>/* jshint onevar:false */
'use strict';
var async = require('async');
var opn = require('opn');
var gen = require('yeoman-generator');
var yosay = require('yosay');
var util = require('util');
var path = require('path');
var updateNotifier = require('update-notifier');
var chalk = require('chalk');
var findup = require('findup');
var fullname = require('fullname');
var _s = require('underscore.string');
var Configstore = require('configstore');
var pkg = require('./package.json');
var conf = new Configstore(pkg.name, {
  generatorRunCount: {}
});

function namespaceToName(val) {
  return val.replace(/(\w+):\w+/, '$1');
}

// The `yo yo` generator provides users with a few common, helpful commands.
var yoyo = module.exports = function (args, options) {
  gen.Base.apply(this, arguments);
  this.insight = options.insight;

  this.insight.track('yoyo', 'init');
  process.once('exit', this._exit.bind(this));
};

util.inherits(yoyo, gen.Base);

// Runs parallel `npm install -g`s for each selected generator.
yoyo.prototype._updateGenerators = function (pkgs) {
  var self = this;

  var resolveGenerators = function (pkg) {
    return function (next) {
      self.spawnCommand('npm', ['install', '-g', pkg])
        .on('error', next)
        .on('exit', next);
    };
  };

  self.insight.track('yoyo', 'update');
  async.parallel(self._.map(pkgs, resolveGenerators), function (err) {
    if (err) {
      self.insight.track('yoyo:err', 'update');
      self.emit('error', err);
      return;
    }

    self.insight.track('yoyo', 'updated');
    self.home({
      refresh: true,
      message:
        'I\'ve just updated your generators. Remember, you can update' +
        '\na specific generator with npm by running:\n' +
        chalk.magenta('\n    npm install -g generator-_______')
    });
  });
};

// Prompts the user to select which generators to update
yoyo.prototype._promptToUpdateGenerators = function () {
  this.prompt([{
    name: '_updateSelectedGenerators',
    message: 'Generators to update',
    type: 'checkbox',
    choices: this._.map(this.pkgs, function (generator) {
      return {name: generator.name, checked: true};
    })
  }], function (answer) {
    this._updateGenerators.call(this, answer._updateSelectedGenerators);
  }.bind(this));
};

// Initializes a generator.
//
// - generator - (string) The generator to initialize.
yoyo.prototype._initGenerator = function (generator, done) {
  console.log(
    chalk.yellow('\nMake sure you are in the directory you want to scaffold into.\n') +
    chalk.dim('This generator can also be run with: ' +
    chalk.blue('yo ' + generator.split(':')[0]) + '\n')
  );

  // save the generator run count
  var generatorName = namespaceToName(generator);
  var generatorRunCount = conf.get('generatorRunCount');
  generatorRunCount[generatorName] = typeof generatorRunCount[generatorName] === 'number' ?
    ++generatorRunCount[generatorName] : 1;
  conf.set('generatorRunCount', generatorRunCount);

  this.insight.track('yoyo', 'run', generator);
  this.composeWith(generator);
  done();
};

// Serves as the response prompt for "Install a generator" as well as simply
// installs a generator if a string is passed in.
//
// - pkgName - (optional) A string that matches the NPM package name.
yoyo.prototype._installGenerator = function (pkgName) {
  if (this._.isString(pkgName)) {
    this.insight.track('yoyo', 'install', pkgName);

    // We know what generator we want to install
    return this.spawnCommand('npm', ['install', '-g', pkgName])
      .on('error', function (err) {
        this.insight.track('yoyo:err', 'install', pkgName);
        this.emit('error', err);
      }.bind(this))
      .on('exit', function () {
        this.insight.track('yoyo', 'installed', pkgName);
        this.home({
          refresh: true,
          message:
            '\nI just installed a generator by running:\n' +
            chalk.blue.bold('\n    npm install -g ' + pkgName)
        });
      }.bind(this));
  }

  this.insight.track('yoyo', 'install');

  this.prompt([{
    name: 'searchTerm',
    message: 'Search NPM for generators'
  }], this._searchNpm.bind(this));
};

// Grabs all of the packages with a `yeoman-generator` keyword on NPM.
//
// - term - (object) Contains the search term & gets passed back to callback().
// - cb   - Callback to execute once generators have been found.
yoyo.prototype._findAllNpmGenerators = function (term, cb) {
  var url = 'http://isaacs.iriscouch.com/registry/_design/' +
    'app/_view/byKeyword?startkey=[%22yeoman-generator%22]' +
    '&endkey=[%22yeoman-generator%22,{}]&group_level=3';

  this.request(url, function (err, res, body) {
    if (err) {
      this.emit('error', err);
      return;
    }

    try {
      this.npmGenerators = JSON.parse(body);
    } catch (err) {
      return this.emit('error', new Error(chalk.bold(
        'A problem occurred contacting the registry.' +
        '\nUnable to parse response: not valid JSON.'
      )));
    }

    cb(term);
  }.bind(this));
};

// Grab out name, and author from git repo of the generator
//
// - generator - (object) Contains npm registry json object.
// - cb   - Callback to execute once generators have been processed.
yoyo.prototype._handleRow = function(generator,cb){
  var url = 'https://skimdb.npmjs.com/registry/' + generator.key[1];
  this.request({url: url, json: true}, function (err, res, body) {
    if (!err && res.statusCode === 200) {
      var packageType = this._isYeomanPackage(body);
      cb(null, {
        name: generator.key[1].replace(/^generator-/, '') + packageType,
        value: generator.key[1],
        author: packageType ? body.author.name : ''
      });
    } else {
      cb(new Error('GitHub fetch failed\n' + err + '\n' + body));
    }
  }.bind(this));
};

// Determine if NPM package is a yeoman package
// - body - object containing github repo information
yoyo.prototype._isYeomanPackage = function(body){
  return body.author &&
  body.author.name === 'The Yeoman Team' ? chalk.green(' :{') : '';
};

//Sorts the NPM Packages in alphabetical order
// - a - first compared NPM package
// - b - second compared NPM package
yoyo.prototype._sortNPMPackage = function(a,b){
      if (a.name < b.name){
         return -1;
      }
      if (a.name > b.name){
        return 1;
      }
      return 0;
};
// Takes a search term, looks it up in the registry, prompts the user with the
// results, allowing them to choose to install it, or go back home.
//
// - term - Object with a 'searchTerm' property containing the term to search
//          NPM for.
yoyo.prototype._searchNpm = function (term) {
  if (!this.npmGenerators) {
    return this._findAllNpmGenerators(term, this._searchNpm.bind(this));
  }

  var pkgs = this.pkgs;
  // Find any matches from NPM.
  var availableGenerators = this.npmGenerators.rows.filter(function(generator){
    return !pkgs[generator.key[1]] && generator.key.join(' ').indexOf(term.searchTerm) > -1;
  });

  async.map(availableGenerators, this._handleRow.bind(this), function(err, choices){
    choices.sort(this._sortNPMPackage);

    var introMessage = 'Sorry, nothing was found';

    if (choices.length > 0){
      introMessage = 'Here\'s what I found. (' + chalk.green(':{') +
      ' represents offical generators)\n  Install one?';
    }

    var resultsPrompt = [{
      name: '_installGenerator',
      type: 'list',
      message: introMessage,
      choices: this._.union(choices, [{
        name: 'Search again',
        value: '_installGenerator'
        }, {
        name: 'Return home',
        value: 'home'
      }])
    }];

    this.prompt(resultsPrompt, function (answer) {
      if (this._.isFunction(this[answer._installGenerator])) {
        return this[answer._installGenerator]();
      }
      this._installGenerator(answer._installGenerator);
    }.bind(this));
  }.bind(this));
};

// Prompts user with a few helpful resources, then opens it in their browser.
yoyo.prototype._findHelp = function () {
  this.insight.track('yoyo', 'help');
  this.prompt([{
    name: 'whereTo',
    type: 'list',
    message:
      'Here are a few helpful resources.\n' +
      '\nI will open the link you select in your browser for you',
    choices: [{
      name: 'Take me to the documentation',
      value: 'http://yeoman.io/learning/index.html'
    }, {
      name: 'View Frequently Asked Questions',
      value: 'http://yeoman.io/learning/faq.html'
    }, {
      name: 'File an issue on GitHub',
      value: 'http://yeoman.io/contributing/opening-issues.html'
    }, {
      name: 'Take me back home, Yo!',
      value: {
        method: 'home',
        args: {
          message: 'I get it, you like learning on your own. I respect that.'
        }
      }
    }]
  }], function (answer) {
    this.insight.track('yoyo', 'help', answer);

    if (this._.isFunction(this[answer.whereTo.method])) {
      this[answer.whereTo.method](answer.whereTo.args);
    } else {
      opn(answer.whereTo);
    }
  }.bind(this));
};

// Serves as a quick escape from the `yo yo` prompts.
yoyo.prototype._exit = function () {
  this.insight.track('yoyo', 'exit');

  var url = 'https://github.com/yeoman/yeoman#team';
  var maxLength = url.length;
  var newLine = new Array(maxLength).join(' ');

  console.log(
    '\n' +
    yosay(
      'Bye from us! Chat soon.' +
      newLine +
      newLine +
      'The Yeoman Team ' + url,
      { maxLength: maxLength }
    )
  );
};

// I'm sorry...
yoyo.prototype._noop = function () {};

// Rolls through all of the generators provided by `env.generators`, finding
// their `package.json` files, then storing them internally in `this.pkgs`.
yoyo.prototype.findGenerators = function () {
  this.pkgs = {};

  var resolveGenerators = function (generator) {
    if (!/(app|all)$/.test(generator.namespace)) {
      return;
    }

    var dir = findup.sync(generator.resolved, 'package.json');
    if (!dir) {
      return;
    }

    var pkg = gen.file.readJSON(path.join(dir, 'package.json'));
    pkg.namespace = generator.namespace;
    pkg.appGenerator = true;
    pkg.prettyName = _s.titleize(_s.humanize(namespaceToName(generator.namespace)));

    pkg.update = updateNotifier({
      packageName: pkg.name,
      packageVersion: pkg.version
    }).update;

    if (pkg.update && pkg.version !== pkg.update.latest) {
      pkg.updateAvailable = true;
    }

    this.pkgs[pkg.name] = pkg;
  };

  this._.each(this.env.getGeneratorsMeta(), resolveGenerators, this);
};

// Display the home `yo` screen, with the intial set of options.
//
// - options - (optional)
//           - message (string) - String to print before prompt.
//           - refresh (bool) - Spawn a new `yo` command.
yoyo.prototype.home = function (options) {
  var done = this.async();

  options = options || {};

  if (options.refresh) {
    this.env.lookup();
    this.findGenerators();
  }

  if (options.message) {
    console.log('\n' + chalk.cyan(options.message) + '\n');
  }

  var defaultChoices = [{
    name: 'Install a generator',
    value: {
      method: '_installGenerator'
    }
  }, {
    name: 'Find some help',
    value: {
      method: '_findHelp'
    }
  }, {
    name: 'Get me out of here!',
    value: {
      method: '_noop'
    }
  }];

  var generatorList = this._.chain(this.pkgs).map(function (generator) {
    if (!generator.appGenerator) {
      return;
    }

    var updateInfo = generator.updateAvailable ? chalk.dim.yellow(' ♥ Update Available!') : '';

    // TODO
    // var updateInfo = generator.updateAvailable ?
    //   chalk.dim.yellow(' ♥ Update Available!') +
    //   chalk.dim.reset(' (Press Space to update)') : '';

    return {
      name: generator.prettyName + updateInfo,
      value: {
        method: '_initGenerator',
        args: generator.namespace
      }
    };
  }).compact().sortBy(function (el) {
    var generatorName = namespaceToName(el.value.args);
    return -conf.get('generatorRunCount')[generatorName] || 0;
  }).value();

  if (generatorList.length) {
    defaultChoices.unshift({
      name: 'Update your generators',
      value: {
        method: '_promptToUpdateGenerators'
      }
    });
  }

  this.insight.track('yoyo', 'home');

  fullname(function (err, name) {
    if (err) {
      done(err);
      return;
    }

    var allo = name ? ('\'Allo ' + name.split(' ')[0] + '! ') : '\'Allo! ';

    this.prompt([{
      name: 'whatNext',
      type: 'list',
      message: allo + 'What would you like to do?',
      choices: this._.flatten([
        new gen.inquirer.Separator('Run a generator'),
        generatorList,
        new gen.inquirer.Separator(),
        defaultChoices,
        new gen.inquirer.Separator()
      ])
    }], function (answer) {
      this[answer.whatNext.method](answer.whatNext.args, done);
    }.bind(this));
  }.bind(this));
};
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Link} from 'react-router'
import _ from 'lodash'
import classnames from 'classnames'
import OnClickOutside from 'shared/components/OnClickOutside'
import {ErrorHandling} from 'src/shared/decorators/errors'

@ErrorHandling
class DashboardSwitcher extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
    }
  }

  handleToggleMenu = () => {
    this.setState({isOpen: !this.state.isOpen})
  }

  handleCloseMenu = () => {
    this.setState({isOpen: false})
  }

  handleClickOutside = () => {
    this.setState({isOpen: false})
  }

  render() {
    const {activeDashboard, names} = this.props
    const {isOpen} = this.state
    const sorted = _.sortBy(names, ({name}) => name.toLowerCase())

    return (

        <button
          className="btn btn-square btn-default btn-sm dropdown-toggle"
          onClick={this.handleToggleMenu}
        >

        </button>

          {sorted.map(({name, link}) => (
            <NameLink
              key={link}
              name={name}
              link={link}
              activeName={activeDashboard}
              onClose={this.handleCloseMenu}
            />
          ))}

    )
  }
}

const NameLink = ({name, link, activeName, onClose}) => (

    <Link to={link} onClick={onClose}>
      {name}
    </Link>

)

const {arrayOf, func, shape, string} = PropTypes

DashboardSwitcher.propTypes = {
  activeDashboard: string.isRequired,
  names: arrayOf(
    shape({
      link: string.isRequired,
      name: string.isRequired,
    })
  ).isRequired,
}

NameLink.propTypes = {
  name: string.isRequired,
  link: string.isRequired,
  activeName: string.isRequired,
  onClose: func.isRequired,
}

export default OnClickOutside(DashboardSwitcher)
<commit_msg>Wrap dashboard quick select with scrolling<commit_after>import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Link} from 'react-router'
import _ from 'lodash'
import classnames from 'classnames'
import OnClickOutside from 'shared/components/OnClickOutside'
import {ErrorHandling} from 'src/shared/decorators/errors'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'

@ErrorHandling
class DashboardSwitcher extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
    }
  }

  handleToggleMenu = () => {
    this.setState({isOpen: !this.state.isOpen})
  }

  handleCloseMenu = () => {
    this.setState({isOpen: false})
  }

  handleClickOutside = () => {
    this.setState({isOpen: false})
  }

  render() {
    const {activeDashboard} = this.props
    const {isOpen} = this.state

    return (

        <button
          className="btn btn-square btn-default btn-sm dropdown-toggle"
          onClick={this.handleToggleMenu}
        >

        </button>

          <FancyScrollbar
            autoHeight={true}
            maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
          >
            {this.sortedList.map(({name, link}) => (
              <NameLink
                key={link}
                name={name}
                link={link}
                activeName={activeDashboard}
                onClose={this.handleCloseMenu}
              />
            ))}
          </FancyScrollbar>

    )
  }

  get sortedList() {
    const {names} = this.props
    return _.sortBy(names, ({name}) => name.toLowerCase())
  }
}

const NameLink = ({name, link, activeName, onClose}) => (

    <Link to={link} onClick={onClose}>
      {name}
    </Link>

)

const {arrayOf, func, shape, string} = PropTypes

DashboardSwitcher.propTypes = {
  activeDashboard: string.isRequired,
  names: arrayOf(
    shape({
      link: string.isRequired,
      name: string.isRequired,
    })
  ).isRequired,
}

NameLink.propTypes = {
  name: string.isRequired,
  link: string.isRequired,
  activeName: string.isRequired,
  onClose: func.isRequired,
}

export default OnClickOutside(DashboardSwitcher)
<|endoftext|>


<commit_before>/*
    Nodes – Arko's Experiment with HTML5 canvas element
    Author: Loïc Cattani (Arko) <loic.cattani@gmail.com>
    Date: December 2010
*/

var canvas;
var context;
var mouse = { x: 0, y: 0, down: false };
var mouseDownTime = 0;
var framerate = 60;
var intervalId; // Will hold the loop reference

function initialize() {

  // Get the canvas element
  canvas = document.getElementById('world');

  if (canvas && canvas.getContext) {

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

    // Register Event Listeners
    document.addEventListener('mousedown', mouseDown, false);
    document.addEventListener('mouseup', mouseUp, false);
    canvas.addEventListener('mousemove', mouseMove, false);
    document.addEventListener('keydown', keyDown, false);
    document.addEventListener('keyup', keyUp, false);
    window.addEventListener('resize', centerCanvas, false);

    // Initiate Nodes World
    NodesWorld.initialize();

    // Override canvas CSS positioning and center canvas on screen
    centerCanvas();

    // Initiate the loop
    start();
  }
}

// Event handler for mouseDown
function mouseDown() {
  mouse.down = true;
  mouseMove();
  mouseDownTime = (new Date).getTime();
  sw = new ShockWave(mouse.x, mouse.y, 0);
  NodesWorld.shock_waves.push(sw);
}

// Event handler for mouseUp
function mouseUp() {
  mouse.down = false;
  mouseMove();
  NodesWorld.shock_waves[NodesWorld.shock_waves.length-1].blast();
}

// Event handler for mouseMove
function mouseMove() {
  mouse.x = event.clientX - (window.innerWidth - canvas.width) / 2;
  mouse.y = event.clientY - (window.innerHeight - canvas.height) / 2;
}

// Event handler for keyDown
function keyDown(event) {
  switch(event.keyCode){
    case 80: // "p" for pause
      toggleLoop();
      event.preventDefault();
      break;
    case 83: // "s" for shockwave
      NodesWorld.blast();
      event.preventDefault();
      break;
  }
  //console.log("KeyCode: " + event.keyCode)
}

// Event handler for keyUp
function keyUp() {
}

// Center the canvas in the window's body
//   (Needed for mouse position to be accurate)
function centerCanvas() {
  canvas.style.position = 'absolute';
  canvas.style.top = Math.round((window.innerHeight - canvas.height - 12) / 2) + 'px'; // 12 = 2 * 6 pixels border
  canvas.style.left = Math.round((window.innerWidth - canvas.width - 12) / 2) + 'px';
}

// Initiate the loop
function start() {
  intervalId = setInterval(loop, 1000/framerate);
}

// Clear the loop
function pause() {
  clearInterval(intervalId);
  intervalId = null;
}

// Toggle loop interval
function toggleLoop() {
  (intervalId) ? pause() : start();
}

// The loop
function loop() {
  NodesWorld.update();
  NodesWorld.draw();
  stats.update();
}

var NodesWorld = new function () {
  // Physics
  this.boundaries = { x: 0, y: 0, width: 940, height: 660 };
  this.drag = 0.002; // Good val: 0.002
  this.gravity = { force: 0.0, direction: Math.PI/2 }; // Good force: 0.2 Direction: Math.PI/2 rad => 90° from +x axis CW

  // Content
  this.node_count = 10000;
  this.nodes = new Array();
  this.shock_waves = new Array();

  this.initialize = function() {
    // Transform gravity to a force vector
    this.gravity = new Vector(this.gravity.force, this.gravity.direction);

    // Fill world with randomly positionned nodes
    for (var i = this.node_count - 1; i >= 0; i--){
      node = new Node(Math.random() * (this.boundaries.width-1), Math.random() * (this.boundaries.height-1));
      this.nodes.push(node);
    }
  }

  // Update world's objects
  this.update = function () {
    // Update nodes
    for( var i = 0, len = this.nodes.length; i < len; i++ ) {
      node = this.nodes[i];
      node.update(this.drag, this.gravity);
    }

    // Update ShockWaves
    for( var i = 0, len = this.shock_waves.length; i < len; i++ ) {
      sw = this.shock_waves[i];
      if (sw) // If shockwave still exists, either update it or remove it from array if alpha == 0
        (sw.color.a == 0) ? this.shock_waves.splice(i,1) : sw.update();
    }
  }

  // Draw world
  this.draw = function () {

    // Clear world
    context.globalCompositeOperation = 'source-over';
    context.fillStyle = "rgba(0,0,0,1)";
    context.fillRect(this.boundaries.x, this.boundaries.y, this.boundaries.width, this.boundaries.height);
    // context.globalCompositeOperation = 'lighter';

    // Draw nodes
    for( var i = 0, len = this.nodes.length; i < len; i++ ) {
      node = this.nodes[i];
      context.fillStyle = node.fillcolor;
      context.fillRect( node.x, node.y, node.size, node.size );
    }

    // Draw shockwaves
    for( var i = 0, len = this.shock_waves.length; i < len; i++ ) {
      sw = this.shock_waves[i];
      context.beginPath();
      context.arc( sw.x, sw.y, sw.outer_radius, 0, Math.PI*2, true );
      context.fillStyle = sw.outer_fillcolor,
      context.fill();
      context.beginPath();
      context.arc( sw.x, sw.y, sw.inner_radius, 0, Math.PI*2, true );
      context.fillStyle = sw.inner_fillcolor;
      context.strokeStyle = sw.inner_strokecolor;
      context.fill();
      context.stroke();
    }
  }

  // Blast away all nodes close to the mouse position
  this.blast = function (magnitude) {
    sw = new ShockWave(mouse.x, mouse.y);
    sw.magnitude = magnitude || 800;
    this.shock_waves.push(sw);
    NodesWorld.shock_waves[NodesWorld.shock_waves.length-1].blast();
  }
}

/* Defines a point in a 2D space */
function Point (x, y) {
  this.x = x || 0;
  this.y = y || 0;
}

/* Returns the distance between this and a given point */
Point.prototype.distanceTo = function (point) {
  a = point.x - this.x;
  b = point.y - this.y;
  return Math.sqrt(a*a + b*b);
}

/* Returns the angle between this and a given point */
Point.prototype.angleTo = function (point) {
  x = point.x - this.x;
  y = point.y - this.y;
  return Math.atan2(y, x);
}

/* Defines a vector in a 2D space */
function Vector (m, a) {
  this.magnitude = m || 0;
  this.angle = a || 0;
  this.x = 0;
  this.y = 0;
  this.updateXY();
}

Vector.prototype.setX = function (x) {
  this.x = x || 0;
  this.updateMA();
}

Vector.prototype.setY = function (y) {
  this.y = y || 0;
  this.updateMA();
}

Vector.prototype.setMagnitude = function (m) {
  this.magnitude = m || 0;
  this.updateXY();
}

Vector.prototype.setAngle = function (a) {
  this.angle = a || 0;
  this.updateXY();
}

Vector.prototype.updateMA = function () {
  x = this.x;
  y = this.y;
  this.magnitude = Math.sqrt(x*x + y*y);
  this.angle = Math.atan2(y,x);
}

Vector.prototype.updateXY = function () {
  this.x = this.magnitude * Math.cos(this.angle);
  this.y = this.magnitude * Math.sin(this.angle);
}

/* Defines a node */
Node.prototype = new Point;
Node.prototype.constructor = Node;
function Node (x, y) {
  Point.call(this, x, y);
  this.velocity = new Vector();
  this.mass = 10; // Can't be 0!
  this.bounce_damp = 0.6;
  this.size = 1;
  this.color = { r: 255, g: 255, b: 255, a: 1};
  this.fillcolor = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+',0.5)';
  this.strokecolor = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+',1)';
}

/* Update a node's position, optionally applying a drag and a force */
Node.prototype.update = function (d, f) {
  this.drag(d);
  //this.applyForce(f);
  this.x += this.velocity.x;
  this.y += this.velocity.y;
  this.checkCollisions();
}

/* Apply the given drag as a coefficient */
Node.prototype.drag = function (d) {
  d = d || 0;
  this.velocity.x = this.velocity.x * ( 1 - d )
  this.velocity.y = this.velocity.y * ( 1 - d )
}

/* Apply the given force as a vector */
Node.prototype.applyForce = function (f) {
  f = f || new Vector();
  this.velocity.x = this.velocity.x + f.x / this.mass;
  this.velocity.y = this.velocity.y + f.y / this.mass;
}

/* Returns the speed in pixels per time interval */
Node.prototype.speed = function () {
  return this.velocity.magnitude;
}

/* Check World's Boundaries Collisions */
Node.prototype.checkCollisions = function () {
  bx = NodesWorld.boundaries.x;
  bw = NodesWorld.boundaries.width - this.size;
  by = NodesWorld.boundaries.y;
  bh = NodesWorld.boundaries.height - this.size;

  // Check if the node's position is outside the x-axis boundaries
  if (this.x < bx || this.x > bw) {
    // Reverse and damp x velocity
    this.velocity.x = (this.velocity.x * -this.bounce_damp);
    // Damp y velocity
    this.velocity.y = (this.velocity.y * this.bounce_damp);
    // Reposition the node in the x axis minus the damp effect
    if (this.x < bx)
      this.x += (1 + this.bounce_damp) * (bx - this.x)
    if (this.x > bw)
      this.x += (1 + this.bounce_damp) * (bw - this.x)
  }

  // Check if the node's position is outside the y-axis boundaries
  if (this.y < by || this.y > bh) {
    // Reverse and damp y velocity
    this.velocity.y = (this.velocity.y * -this.bounce_damp);
    // Damp x velocity
    this.velocity.x = (this.velocity.x * this.bounce_damp);
    // Reposition the node in the y axis minus the damp effect
    if (this.y < by)
      this.y += (1 + this.bounce_damp) * (by - this.y)
    if (this.y > bh)
      this.y += (1 + this.bounce_damp) * (bh - this.y)
  }
}

/* Defines a shock wave */
ShockWave.prototype = new Point;
ShockWave.prototype.constructor = ShockWave;
function ShockWave (x, y) {
  Point.call(this, x, y);
  this.magnitude = 0;
  this.inner_radius = 20;
  this.outer_radius = 60;
  this.color = randomColor();
  this.inner_fillcolor = 'rgba(255,255,255,0.1)';
  this.inner_strokecolor = 'rgba(255,255,255,0.8)';
  this.outer_fillcolor = 'rgba(255,255,255,0)';
  this.growing = 1;

}

/* Update the shock wave */
ShockWave.prototype.update = function () {
  if (this.growing) {
    now = (new Date).getTime();
    this.magnitude = ((now - mouseDownTime) / 1000) * 800;
    this.inner_radius = this.magnitude / 10
  } else {
    this.inner_radius += (this.magnitude / 10 - this.inner_radius)/5
    this.outer_radius += (this.magnitude / 3.2 - this.outer_radius)/8
    this.color.a -= (this.color.a/10)
    this.inner_fillcolor = 'rgba(255,255,255,'+this.color.a/4+')';
    this.inner_strokecolor = 'rgba(255,255,255,'+this.color.a+')';
    this.outer_fillcolor = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+','+this.color.a/3+')';
    if (this.color.a < 0.02)
      this.color.a = 0;
  };
}

ShockWave.prototype.blast = function () {
  this.growing = 0;
  this.inner_radius = this.magnitude / 40;
  this.outer_radius = this.magnitude / 12;

  /* Trigger the shock wave */
  for( var i = 0, len = NodesWorld.nodes.length; i < len; i++ ) {
    node = NodesWorld.nodes[i];
    d = this.distanceTo(node);
    m = this.magnitude * 1/d;
    a = this.angleTo(node);
    f = new Vector(m, a);
    node.applyForce(f);
  }
}

function randomColor() {
  return {r: Math.round(Math.random()*255), g: Math.round(Math.random()*255), b: Math.round(Math.random()*255), a: 1};
}

initialize();
<commit_msg>Fixed shockwave with magnitude 0 when triggered during pause<commit_after>/*
    Nodes – Arko's Experiment with HTML5 canvas element
    Author: Loïc Cattani (Arko) <loic.cattani@gmail.com>
    Date: December 2010
*/

var canvas;
var context;
var mouse = { x: 0, y: 0, down: false };
var mouseDownTime = 0;
var framerate = 60;
var intervalId; // Will hold the loop reference

function initialize() {

  // Get the canvas element
  canvas = document.getElementById('world');

  if (canvas && canvas.getContext) {

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

    // Register Event Listeners
    document.addEventListener('mousedown', mouseDown, false);
    document.addEventListener('mouseup', mouseUp, false);
    canvas.addEventListener('mousemove', mouseMove, false);
    document.addEventListener('keydown', keyDown, false);
    document.addEventListener('keyup', keyUp, false);
    window.addEventListener('resize', centerCanvas, false);

    // Initiate Nodes World
    NodesWorld.initialize();

    // Override canvas CSS positioning and center canvas on screen
    centerCanvas();

    // Initiate the loop
    start();
  }
}

// Event handler for mouseDown
function mouseDown() {
  mouse.down = true;
  mouseMove();
  mouseDownTime = (new Date).getTime();
  sw = new ShockWave(mouse.x, mouse.y, 0);
  NodesWorld.shock_waves.push(sw);
}

// Event handler for mouseUp
function mouseUp() {
  mouse.down = false;
  mouseMove();
  NodesWorld.shock_waves[NodesWorld.shock_waves.length-1].blast();
}

// Event handler for mouseMove
function mouseMove() {
  mouse.x = event.clientX - (window.innerWidth - canvas.width) / 2;
  mouse.y = event.clientY - (window.innerHeight - canvas.height) / 2;
}

// Event handler for keyDown
function keyDown(event) {
  switch(event.keyCode){
    case 80: // "p" for pause
      toggleLoop();
      event.preventDefault();
      break;
    case 83: // "s" for shockwave
      NodesWorld.blast();
      event.preventDefault();
      break;
  }
  //console.log("KeyCode: " + event.keyCode)
}

// Event handler for keyUp
function keyUp() {
}

// Center the canvas in the window's body
//   (Needed for mouse position to be accurate)
function centerCanvas() {
  canvas.style.position = 'absolute';
  canvas.style.top = Math.round((window.innerHeight - canvas.height - 12) / 2) + 'px'; // 12 = 2 * 6 pixels border
  canvas.style.left = Math.round((window.innerWidth - canvas.width - 12) / 2) + 'px';
}

// Initiate the loop
function start() {
  intervalId = setInterval(loop, 1000/framerate);
}

// Clear the loop
function pause() {
  clearInterval(intervalId);
  intervalId = null;
}

// Toggle loop interval
function toggleLoop() {
  (intervalId) ? pause() : start();
}

// The loop
function loop() {
  NodesWorld.update();
  NodesWorld.draw();
  stats.update();
}

var NodesWorld = new function () {
  // Physics
  this.boundaries = { x: 0, y: 0, width: 940, height: 660 };
  this.drag = 0.002; // Good val: 0.002
  this.gravity = { force: 0.0, direction: Math.PI/2 }; // Good force: 0.2 Direction: Math.PI/2 rad => 90° from +x axis CW

  // Content
  this.node_count = 10000;
  this.nodes = new Array();
  this.shock_waves = new Array();

  this.initialize = function() {
    // Transform gravity to a force vector
    this.gravity = new Vector(this.gravity.force, this.gravity.direction);

    // Fill world with randomly positionned nodes
    for (var i = this.node_count - 1; i >= 0; i--){
      node = new Node(Math.random() * (this.boundaries.width-1), Math.random() * (this.boundaries.height-1));
      this.nodes.push(node);
    }
  }

  // Update world's objects
  this.update = function () {
    // Update nodes
    for( var i = 0, len = this.nodes.length; i < len; i++ ) {
      node = this.nodes[i];
      node.update(this.drag, this.gravity);
    }

    // Update ShockWaves
    for( var i = 0, len = this.shock_waves.length; i < len; i++ ) {
      sw = this.shock_waves[i];
      if (sw) // If shockwave still exists, either update it or remove it from array if alpha == 0
        (sw.color.a == 0) ? this.shock_waves.splice(i,1) : sw.update();
    }
  }

  // Draw world
  this.draw = function () {

    // Clear world
    context.globalCompositeOperation = 'source-over';
    context.fillStyle = "rgba(0,0,0,1)";
    context.fillRect(this.boundaries.x, this.boundaries.y, this.boundaries.width, this.boundaries.height);
    // context.globalCompositeOperation = 'lighter';

    // Draw nodes
    for( var i = 0, len = this.nodes.length; i < len; i++ ) {
      node = this.nodes[i];
      context.fillStyle = node.fillcolor;
      context.fillRect( node.x, node.y, node.size, node.size );
    }

    // Draw shockwaves
    for( var i = 0, len = this.shock_waves.length; i < len; i++ ) {
      sw = this.shock_waves[i];
      context.beginPath();
      context.arc( sw.x, sw.y, sw.outer_radius, 0, Math.PI*2, true );
      context.fillStyle = sw.outer_fillcolor,
      context.fill();
      context.beginPath();
      context.arc( sw.x, sw.y, sw.inner_radius, 0, Math.PI*2, true );
      context.fillStyle = sw.inner_fillcolor;
      context.strokeStyle = sw.inner_strokecolor;
      context.fill();
      context.stroke();
    }
  }

  // Blast away all nodes close to the mouse position
  this.blast = function (magnitude) {
    sw = new ShockWave(mouse.x, mouse.y);
    sw.magnitude = magnitude || 800;
    this.shock_waves.push(sw);
    NodesWorld.shock_waves[NodesWorld.shock_waves.length-1].blast();
  }
}

/* Defines a point in a 2D space */
function Point (x, y) {
  this.x = x || 0;
  this.y = y || 0;
}

/* Returns the distance between this and a given point */
Point.prototype.distanceTo = function (point) {
  a = point.x - this.x;
  b = point.y - this.y;
  return Math.sqrt(a*a + b*b);
}

/* Returns the angle between this and a given point */
Point.prototype.angleTo = function (point) {
  x = point.x - this.x;
  y = point.y - this.y;
  return Math.atan2(y, x);
}

/* Defines a vector in a 2D space */
function Vector (m, a) {
  this.magnitude = m || 0;
  this.angle = a || 0;
  this.x = 0;
  this.y = 0;
  this.updateXY();
}

Vector.prototype.setX = function (x) {
  this.x = x || 0;
  this.updateMA();
}

Vector.prototype.setY = function (y) {
  this.y = y || 0;
  this.updateMA();
}

Vector.prototype.setMagnitude = function (m) {
  this.magnitude = m || 0;
  this.updateXY();
}

Vector.prototype.setAngle = function (a) {
  this.angle = a || 0;
  this.updateXY();
}

Vector.prototype.updateMA = function () {
  x = this.x;
  y = this.y;
  this.magnitude = Math.sqrt(x*x + y*y);
  this.angle = Math.atan2(y,x);
}

Vector.prototype.updateXY = function () {
  this.x = this.magnitude * Math.cos(this.angle);
  this.y = this.magnitude * Math.sin(this.angle);
}

/* Defines a node */
Node.prototype = new Point;
Node.prototype.constructor = Node;
function Node (x, y) {
  Point.call(this, x, y);
  this.velocity = new Vector();
  this.mass = 10; // Can't be 0!
  this.bounce_damp = 0.6;
  this.size = 1;
  this.color = { r: 255, g: 255, b: 255, a: 1};
  this.fillcolor = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+',0.5)';
  this.strokecolor = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+',1)';
}

/* Update a node's position, optionally applying a drag and a force */
Node.prototype.update = function (d, f) {
  this.drag(d);
  //this.applyForce(f);
  this.x += this.velocity.x;
  this.y += this.velocity.y;
  this.checkCollisions();
}

/* Apply the given drag as a coefficient */
Node.prototype.drag = function (d) {
  d = d || 0;
  this.velocity.x = this.velocity.x * ( 1 - d )
  this.velocity.y = this.velocity.y * ( 1 - d )
}

/* Apply the given force as a vector */
Node.prototype.applyForce = function (f) {
  f = f || new Vector();
  this.velocity.x = this.velocity.x + f.x / this.mass;
  this.velocity.y = this.velocity.y + f.y / this.mass;
}

/* Returns the speed in pixels per time interval */
Node.prototype.speed = function () {
  return this.velocity.magnitude;
}

/* Check World's Boundaries Collisions */
Node.prototype.checkCollisions = function () {
  bx = NodesWorld.boundaries.x;
  bw = NodesWorld.boundaries.width - this.size;
  by = NodesWorld.boundaries.y;
  bh = NodesWorld.boundaries.height - this.size;

  // Check if the node's position is outside the x-axis boundaries
  if (this.x < bx || this.x > bw) {
    // Reverse and damp x velocity
    this.velocity.x = (this.velocity.x * -this.bounce_damp);
    // Damp y velocity
    this.velocity.y = (this.velocity.y * this.bounce_damp);
    // Reposition the node in the x axis minus the damp effect
    if (this.x < bx)
      this.x += (1 + this.bounce_damp) * (bx - this.x)
    if (this.x > bw)
      this.x += (1 + this.bounce_damp) * (bw - this.x)
  }

  // Check if the node's position is outside the y-axis boundaries
  if (this.y < by || this.y > bh) {
    // Reverse and damp y velocity
    this.velocity.y = (this.velocity.y * -this.bounce_damp);
    // Damp x velocity
    this.velocity.x = (this.velocity.x * this.bounce_damp);
    // Reposition the node in the y axis minus the damp effect
    if (this.y < by)
      this.y += (1 + this.bounce_damp) * (by - this.y)
    if (this.y > bh)
      this.y += (1 + this.bounce_damp) * (bh - this.y)
  }
}

/* Defines a shock wave */
ShockWave.prototype = new Point;
ShockWave.prototype.constructor = ShockWave;
function ShockWave (x, y) {
  Point.call(this, x, y);
  this.magnitude = 0;
  this.inner_radius = 20;
  this.outer_radius = 60;
  this.color = randomColor();
  this.inner_fillcolor = 'rgba(255,255,255,0.1)';
  this.inner_strokecolor = 'rgba(255,255,255,0.8)';
  this.outer_fillcolor = 'rgba(255,255,255,0)';
  this.growing = 1;

}

/* Update the shock wave */
ShockWave.prototype.update = function () {
  if (this.growing) {
    now = (new Date).getTime();
    this.magnitude = ((now - mouseDownTime) / 1000) * 800;
    this.inner_radius = this.magnitude / 10
  } else {
    this.inner_radius += (this.magnitude / 10 - this.inner_radius)/5
    this.outer_radius += (this.magnitude / 3.2 - this.outer_radius)/8
    this.color.a -= (this.color.a/10)
    this.inner_fillcolor = 'rgba(255,255,255,'+this.color.a/4+')';
    this.inner_strokecolor = 'rgba(255,255,255,'+this.color.a+')';
    this.outer_fillcolor = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+','+this.color.a/3+')';
    if (this.color.a < 0.02)
      this.color.a = 0;
  };
}

ShockWave.prototype.blast = function () {
  now = (new Date).getTime();
  this.magnitude = ((now - mouseDownTime) / 1000) * 800;
  this.growing = 0;
  this.inner_radius = this.magnitude / 40;
  this.outer_radius = this.magnitude / 12;

  /* Trigger the shock wave */
  for( var i = 0, len = NodesWorld.nodes.length; i < len; i++ ) {
    node = NodesWorld.nodes[i];
    d = this.distanceTo(node);
    m = this.magnitude * 1/d;
    a = this.angleTo(node);
    f = new Vector(m, a);
    node.applyForce(f);
  }
}

function randomColor() {
  return {r: Math.round(Math.random()*255), g: Math.round(Math.random()*255), b: Math.round(Math.random()*255), a: 1};
}

initialize();
<|endoftext|>