<commit_before>/**
 * Clientside routes
 */

this.Routes = function() {
  // The container of the nav and login component
  this.headerSelector = ".header";
  this.header = $(this.headerSelector).get(0);

  // Render structure templates that don't change on every route change
  this.renderPermanentTemplates();

  // Setup Google Analytics (change UA-XXXXX-X to your own Google Analytics number!)
  this._gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
  (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
  g.src='//www.google-analytics.com/ga.js';
  s.parentNode.insertBefore(g,s)}(document,'script'));

  // Bind a Google Analytics track event to every page change
  var me = this;
  Router.onRun(function() {
    // Let Google Analytics know that the page has changed
    var url = Router.current().url;
    me._gaq.push(['_trackPageview', url])

    this.next();
  });

  Router.configure({
    notFoundTemplate: Template['404']
  });

  this.define();
};

this.Routes.prototype = {
  // Define all routes
  define: function() {
    var me = this;

    Router.route('/', function () {
        me.go(this, Template.home);
    });
    Router.route('/colors', function () {
        me.go(this, Template.colors);
    });
    Router.route('/cars', function () {
        me.go(this, Template.cars);
    });
    Router.route('/cars/car/:id', function () {
        me.go(this, Template.carsDetail, false, {id: this.params.id});
    });
    Router.route('/account', function () {
        me.go(this, Template.account, true);
    });
  },

  // Actually changes the page by creating the view and inserting it
  go: function (router, template, internal, templateData) {
    if (!template) {
      template = Template.home;
    }

    // Pages that are "internal" can only be viewed by a logged in user
    if (internal && !Meteor.userId()) {
      router.render(Template['404']);
      return;
    }

    // If all is well, go to the requested page!
    if (!internal || Meteor.userId()) {
      // Set data for this template to use
      Session.set('templateData', templateData || {});
      router.render(template);
    }
  },

  // Code to run before any action happens
  renderPermanentTemplates: function() {
    // Create a component view that renders in the page template, on every page
    this.header.innerHTML = '';
    Blaze.render(Template.componentHeader, this.header);
  }
};
<commit_msg>Updated router comment language<commit_after>/**
 * Clientside routes
 */

this.Routes = function() {
  // The container of the nav and login component
  this.headerSelector = ".header";
  this.header = $(this.headerSelector).get(0);

  // Render structure templates that don't change on every route change
  this.renderPermanentTemplates();

  // Setup Google Analytics (change UA-XXXXX-X to your own Google Analytics number!)
  this._gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
  (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
  g.src='//www.google-analytics.com/ga.js';
  s.parentNode.insertBefore(g,s)}(document,'script'));

  // Bind a Google Analytics track event to every page change
  var me = this;
  Router.onRun(function() {
    // Let Google Analytics know that the page has changed
    var url = Router.current().url;
    me._gaq.push(['_trackPageview', url])

    this.next();
  });

  Router.configure({
    notFoundTemplate: Template['404']
  });

  this.define();
};

this.Routes.prototype = {
  // Define all routes
  define: function() {
    var me = this;

    Router.route('/', function () {
        me.go(this, Template.home);
    });
    Router.route('/colors', function () {
        me.go(this, Template.colors);
    });
    Router.route('/cars', function () {
        me.go(this, Template.cars);
    });
    Router.route('/cars/car/:id', function () {
        me.go(this, Template.carsDetail, false, {id: this.params.id});
    });
    Router.route('/account', function () {
        me.go(this, Template.account, true);
    });
  },

  // Actually changes the page by rendering the template
  go: function (router, template, internal, templateData) {
    if (!template) {
      template = Template.home;
    }

    // Pages that are "internal" can only be viewed by a logged in user
    if (internal && !Meteor.userId()) {
      router.render(Template['404']);
      return;
    }

    // If all is well, go to the requested page!
    if (!internal || Meteor.userId()) {
      // Set data for this template to use
      Session.set('templateData', templateData || {});
      router.render(template);
    }
  },

  // Code to run before any action happens
  renderPermanentTemplates: function() {
    // Create a component view that renders in the page template, on every page
    this.header.innerHTML = '';
    Blaze.render(Template.componentHeader, this.header);
  }
};
<|endoftext|>


<commit_before>// @flow

const path = require('path'),
  spawn = require('cross-spawn'),
  fs = require('fs-extra'),
  _ = require('lodash'),
  Murmur = require('murmur-hash-js'),
  Version = require('./version.js'),
  supportsColor = require('supports-color');

function prepareCompiledJsFile(pipeFilename /*: string */, dest /*: string */) {
  return Promise.all([
    readUtf8(path.join(__dirname, '..', 'templates', 'before.js')),
    readUtf8(dest),
    readUtf8(path.join(__dirname, '..', 'templates', 'after.js')),
  ]).then(([before, content, after]) => {
    return new Promise((resolve, reject) => {
      const finalContent = [
        before,
        'var Elm = (function(module) { ',
        content,
        'return this.Elm;',
        '})({});',
        'var pipeFilename = ' + JSON.stringify(pipeFilename) + ';',
        after,
      ].join('\n');
      return fs.writeFile(dest, finalContent, err => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  });
}

function readUtf8(filepath) {
  return new Promise((resolve, reject) => {
    fs.readFile(filepath, { encoding: 'utf8' }, (err, contents) => {
      if (err) {
        reject(err);
      } else {
        resolve(contents);
      }
    });
  });
}

function generateElmJson(
  projectRootDir /*: string */,
  testRootDir /*: string */,
  pathToElmBinary /*: string */,
  filePaths /*: Array<string> */,
  hasBeenGivenCustomGlobs /*: boolean */
) {
  const generatedCodeDir = path.resolve(
    path.join(
      projectRootDir,
      'elm-stuff',
      'generated-code',
      'elm-explorations',
      'test',
      '0.19.1'
    )
  );
  const elmJsonPath = path.resolve(path.join(projectRootDir, 'elm.json'));
  const generatedSrc = path.join(generatedCodeDir, 'src');

  var projectElmJson = {};

  try {
    projectElmJson = fs.readJsonSync(elmJsonPath);
  } catch (err) {
    console.error('Error reading elm.json: ' + err);
    process.exit(1);
  }
  var isPackageProject = projectElmJson.type === 'package';

  // if we were given file globs, we don't need to check the tests/ directory exists
  // this is only for elm applications, which people may need to introduce slowly into their apps
  // for packages, we stick with the existing behaviour and assume tests are in tests/ so do the check always
  const needToCareAboutTestsDir =
    hasBeenGivenCustomGlobs === false || isPackageProject === true;

  // we add the tests dir as a source if:
  // - we decided above we need to care about it
  // - if it exists on disk: this supports the case where we have all our tests in tests/ but
  //   want to pass a glob to only run one of the test files
  const shouldAddTestsDirAsSource =
    needToCareAboutTestsDir ||
    fs.existsSync(path.join(projectRootDir, 'tests'));

  if (needToCareAboutTestsDir) {
    if (!fs.existsSync(testRootDir)) {
      console.error(
        'Error: ' +
          testRootDir +
          ' does not exist. Please create a tests/ directory in your project root!'
      );
      process.exit(1);
    }

    if (!fs.lstatSync(testRootDir).isDirectory()) {
      console.error(
        'Error: ' +
          testRootDir +
          ' exists, but it is not a directory. Please create a tests/ directory in your project root!'
      );
      process.exit(1);
    }
  }

  fs.mkdirpSync(generatedCodeDir);
  fs.mkdirpSync(generatedSrc);

  var testElmJson = {
    type: 'application',
    'source-directories': [], // these are added below
    'elm-version': '0.19.1',
    dependencies: {
      direct: {},
      indirect: {},
    },
    'test-dependencies': {
      direct: {},
      indirect: {},
    },
  };

  var nodeTestRunnerElmJsonPath = path.resolve(
    path.join(__dirname, '..', 'elm', 'elm.json')
  );
  var nodeTestRunnerElmJson = fs.readJsonSync(nodeTestRunnerElmJsonPath);
  // we want to use the version of elm-explorations/test that the user
  // specifies in their own test-dependencies. everything else needs to
  // be included for the test runner to compile.
  delete nodeTestRunnerElmJson.dependencies.direct['elm-explorations/test'];
  addDirectDependencies(
    nodeTestRunnerElmJson['dependencies']['direct'],
    isPackageProject,
    testElmJson
  );
  addIndirectDependencies(
    nodeTestRunnerElmJson['dependencies']['indirect'],
    testElmJson
  );

  if (isPackageProject) {
    addDirectDependencies(
      projectElmJson['dependencies'],
      isPackageProject,
      testElmJson
    );
    addDirectDependencies(
      projectElmJson['test-dependencies'],
      isPackageProject,
      testElmJson
    );
    // package projects don't explicitly list their transitive dependencies,
    // to we have to figure out what they are.  We write the elm.json that
    // we have so far, and run elm to see what it thinks is missing.
    fs.writeFileSync(
      path.join(generatedCodeDir, 'elm.json'),
      JSON.stringify(testElmJson, null, 4)
    );
    var missingDeps = askElmForMissingTransitiveDependencies(
      pathToElmBinary,
      generatedCodeDir
    );
    addIndirectDependencies(missingDeps, testElmJson);
  } else {
    addDirectDependencies(
      projectElmJson['dependencies']['direct'],
      isPackageProject,
      testElmJson
    );
    addIndirectDependencies(
      projectElmJson['dependencies']['indirect'],
      testElmJson
    );
    addDirectDependencies(
      projectElmJson['test-dependencies']['direct'],
      isPackageProject,
      testElmJson
    );
    addIndirectDependencies(
      projectElmJson['test-dependencies']['indirect'],
      testElmJson
    );
  }

  // Make all the source-directories absolute, and introduce a new one.
  var projectSourceDirs;
  if (isPackageProject) {
    projectSourceDirs = ['./src'];
  } else {
    projectSourceDirs = projectElmJson['source-directories'];
  }
  var sourceDirs /*: Array<string> */ = projectSourceDirs
    .map(function(src) {
      return path.resolve(path.join(projectRootDir, src));
    })
    .concat(shouldAddTestsDirAsSource ? [testRootDir] : []);

  testElmJson['source-directories'] = [
    // Include elm-stuff/generated-sources - since we'll be generating sources in there.
    generatedSrc,

    // NOTE: we must include node-test-runner's Elm source as a source-directory
    // instead of adding it as a dependency so that it can include port modules
    path.resolve(path.join(__dirname, '..', 'elm', 'src')),
  ]
  .concat(sourceDirs)
  .filter(
    // When running node-test-runner's own test suite, the node-test-runner/src folder
    // will get added twice: once because it's the source-directory of the packge being tested,
    // and once because elm-test will always add it.
    // To prevent elm from being confused, we need to remove the duplicate when this happens.
    function(value, index, self) {
      return self.indexOf(value) === index;
    }
  ).map(function(absolutePath) {
    // These all need to be relative paths. Otherwise, there's a bug in elm make
    // (as of 0.19.1-alpha4) where certain .elmi files wouldn't get generated.
    //
    // SSCCE: https://gist.github.com/rtfeldman/c0a068794b2e36d350c00357f458c50f
    //
    // Relative paths also have the nice benefit that if the user moves their
    // directory, this doesn't break.
    return path.relative(generatedCodeDir, absolutePath);
  });

  // Generate the new elm.json
  fs.writeFileSync(
    path.join(generatedCodeDir, 'elm.json'),
    JSON.stringify(testElmJson, null, 4)
  );

  return [generatedCodeDir, generatedSrc, sourceDirs];
}

function addDirectDependencies(
  deps /*: Object */,
  isPackageProject /*: boolean */,
  testElmJson /*: Object */
) {
  Object.keys(deps).forEach(function(name) {
    var version = deps[name];
    if (isPackageProject) {
      // Use the lowest version in the range.
      // NOTE: technically this doesn't work if someone does something weird like:
      //
      // "2.0.0 < v < 3.0.0"
      //
      // ...but we're choosing not to support that right now.
      version = version.split(' ')[0];
    }
    if (testElmJson['dependencies']['direct'].hasOwnProperty(name)) {
      var existingVersion = testElmJson['dependencies']['direct'][name];

      // If we have a clash, choose the higher of the two versions.
      // This may not work! It's entirely possible that the result won't
      // compile. We're going to try it and see what happens.
      version = Version.getHigherVersion(version, existingVersion);
    }
    testElmJson['dependencies']['direct'][name] = version;
  });
}

function addIndirectDependencies(deps, testElmJson /*: Object */) {
  Object.keys(deps).forEach(function(name) {
    if (testElmJson['dependencies']['direct'].hasOwnProperty(name)) {
      // already a normal dep
    } else {
      var version = deps[name];

      if (testElmJson['dependencies']['indirect'].hasOwnProperty(name)) {
        var existingVersion = testElmJson['dependencies']['indirect'][name];

        // If we have a clash, choose the higher of the two versions.
        // This may not work! It's entirely possible that the result won't
        // compile. We're going to try it and see what happens.
        version = Version.getHigherVersion(version, existingVersion);
      }

      testElmJson['dependencies']['indirect'][name] = version;
    }
  });
}
function askElmForMissingTransitiveDependencies(
  pathToElmBinary,
  pathToElmProject
) {
  var result = spawn.sync(pathToElmBinary, ['make', '--report=json'], {
    silent: true,
    env: process.env,
    cwd: pathToElmProject,
  });

  if (result.stderr == null) {
    console.error(
      'No output received from elm make when searching for indirect dependencies.'
    );
    process.exit(1);
  }

  // TODO: hopefully the next Elm 0.19 beta will print the JSON to stdout instead
  var output = result.stderr.toString();

  // TODO: hopefully the next Elm 0.19 beta will not have this message (only occurs on linux) mixed in with the JSON output
  output = output.replace(
    /^elm:.*no version information available \(required by elm\)\n/,
    ''
  );

  var report = JSON.parse(output);
  if (report.type === 'error' && report.title === 'NO INPUT') {
    // all transtive dependencies are listed already (elm is complaining that we didn't tell it which .elm file to build, which happens after the dependency validations)
    return {};
  } else if (
    report.type === 'error' &&
    report.title === 'MISSING DEPENDENCIES'
  ) {
    var missingDeps = {};
    // parse the missing dependencies and versions from the error report
    report.message[1].string.replace(/"([^"]*)": "([^"]*)"/g, function(
      _,
      name,
      version
    ) {
      missingDeps[name] = version;
    });
    return missingDeps;
  } else {
    console.error(
      "elm-test internal error: got an unexpected result from 'elm make' when validating transitive dependencies.  Please report this at https://github.com/rtfeldman/node-test-runner/issues"
    );
    process.exit(1);
    return {}; // This makes flow happy because it doesn't know process.exit will stop everything
  }
}

function generateMainModule(
  fuzz /*: number */,
  seed /*: number */,
  report /*: string */,
  testFilePaths /*: Array<string> */,
  tests /*: Array<Object> */,
  generatedSrc /*: string */,
  processes /*: number */
) {
  if (tests.length === 0) {
    const errorMessage =
      testFilePaths.length > 0
        ? 'I couldn\'t find any exposed values of type Test in files matching "' +
          testFilePaths.toString() +
          '"\n\nMaybe try running elm-test with no arguments?'
        : "I couldn't find any exposed values of type Test in any *.elm files in the tests/ directory of your project's root directory.\n\nTo generate some initial tests to get things going, run elm-test init";

    console.error(errorMessage);
    process.exit(1);
  }

  // Building things like:
  //
  // import MyTests
  //
  // MyTests.suite
  const imports = _.map(tests, function(test) {
    return 'import ' + test.name;
  });
  const testList = _.map(tests, function(mod) {
    return (
      '    Test.describe "' +
      mod.name +
      '" [' +
      _.map(mod.tests, function(test) {
        return mod.name + '.' + test;
      }).join(',\n    ') +
      ']'
    );
  });

  function sanitizedToString(str) {
    return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
  }

  function getReportCode() {
    switch (report) {
      case 'json':
        return 'JsonReport';
      case 'junit':
        return 'JUnitReport';
      default:
        if (supportsColor) {
          return '(ConsoleReport UseColor)';
        } else {
          return '(ConsoleReport Monochrome)';
        }
    }
  }

  if (isNaN(seed)) {
    seed = Math.floor(Math.random() * 407199254740991) + 1000;
  }

  var opts = {
    fuzz: isNaN(fuzz) ? 'Nothing' : 'Just ' + fuzz,
    seed: seed,
    report: getReportCode(),
    paths: testFilePaths.map(sanitizedToString).join(','),
  };

  var optsCode =
    '{ runs = ' +
    opts.fuzz +
    ', report = ' +
    opts.report +
    ', seed = ' +
    opts.seed +
    ', processes = ' +
    processes +
    ', paths = [' +
    opts.paths +
    ']}';

  const testFileBody = [
    imports.join('\n'),
    '',
    'import Test.Reporter.Reporter exposing (Report(..))',
    'import Console.Text exposing (UseColor(..))',
    'import Test.Runner.Node',
    'import Test',
    '',
    'main : Test.Runner.Node.TestProgram',
    'main =',
    '    [ ' + testList.join(', ') + ' ]',
    '        |> Test.concat',
    '        |> Test.Runner.Node.run ' + optsCode,
  ].join('\n');

  // Generate a filename that incorporates the hash of file contents.
  // This way, if you run e.g. `elm-test Foo.elm` and then `elm-test Bar.elm`
  // and then re-run `elm-test Foo.elm` we still have a cached `Main` for
  // `Foo.elm` (assuming none of its necessary imports have changed - and
  // why would they?) so we don't have to recompile it.
  const salt = Murmur.murmur3(testFileBody);
  const moduleName = 'Main' + salt;
  const mainPath = path.join(generatedSrc, 'Test', 'Generated');
  const mainFile = path.join(mainPath, moduleName + '.elm');
  // We'll be putting the generated Main in something like this:
  //
  // my-project-name/elm-stuff/generated-code/elm-community/elm-test/src/Test/Generated/Main123456.elm
  const testFileContents = [
    'module Test.Generated.' + moduleName + ' exposing (main)',
    testFileBody,
  ].join('\n\n');

  // Make sure src/Test/Generated/ exists so we can write the file there.
  fs.mkdirpSync(mainPath);

  // Always write the file, in order to update its timestamp. This is important,
  // because if we run `elm-make Main123456.elm` and that file's timestamp did
  // not change, elm-make will short-circuit and not recompile *anything* - even
  // if some of Main's dependencies (such as an individual test file) changed.
  fs.writeFileSync(mainFile, testFileContents);

  return mainFile;
}

module.exports = { prepareCompiledJsFile, generateElmJson, generateMainModule };
<commit_msg>Read elm-test version from package.json<commit_after>// @flow

const path = require('path'),
  spawn = require('cross-spawn'),
  fs = require('fs-extra'),
  _ = require('lodash'),
  Murmur = require('murmur-hash-js'),
  Version = require('./version.js'),
  supportsColor = require('supports-color'),
  packageInfo = require('../package.json'),
  elmTestVersion = packageInfo.version;

function prepareCompiledJsFile(pipeFilename /*: string */, dest /*: string */) {
  return Promise.all([
    readUtf8(path.join(__dirname, '..', 'templates', 'before.js')),
    readUtf8(dest),
    readUtf8(path.join(__dirname, '..', 'templates', 'after.js')),
  ]).then(([before, content, after]) => {
    return new Promise((resolve, reject) => {
      const finalContent = [
        before,
        'var Elm = (function(module) { ',
        content,
        'return this.Elm;',
        '})({});',
        'var pipeFilename = ' + JSON.stringify(pipeFilename) + ';',
        after,
      ].join('\n');
      return fs.writeFile(dest, finalContent, err => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  });
}

function readUtf8(filepath) {
  return new Promise((resolve, reject) => {
    fs.readFile(filepath, { encoding: 'utf8' }, (err, contents) => {
      if (err) {
        reject(err);
      } else {
        resolve(contents);
      }
    });
  });
}

function generateElmJson(
  projectRootDir /*: string */,
  testRootDir /*: string */,
  pathToElmBinary /*: string */,
  filePaths /*: Array<string> */,
  hasBeenGivenCustomGlobs /*: boolean */
) {
  const generatedCodeDir = path.resolve(
    path.join(
      projectRootDir,
      'elm-stuff',
      'generated-code',
      'elm-explorations',
      'test',
      elmTestVersion
    )
  );
  const elmJsonPath = path.resolve(path.join(projectRootDir, 'elm.json'));
  const generatedSrc = path.join(generatedCodeDir, 'src');

  var projectElmJson = {};

  try {
    projectElmJson = fs.readJsonSync(elmJsonPath);
  } catch (err) {
    console.error('Error reading elm.json: ' + err);
    process.exit(1);
  }
  var isPackageProject = projectElmJson.type === 'package';

  // if we were given file globs, we don't need to check the tests/ directory exists
  // this is only for elm applications, which people may need to introduce slowly into their apps
  // for packages, we stick with the existing behaviour and assume tests are in tests/ so do the check always
  const needToCareAboutTestsDir =
    hasBeenGivenCustomGlobs === false || isPackageProject === true;

  // we add the tests dir as a source if:
  // - we decided above we need to care about it
  // - if it exists on disk: this supports the case where we have all our tests in tests/ but
  //   want to pass a glob to only run one of the test files
  const shouldAddTestsDirAsSource =
    needToCareAboutTestsDir ||
    fs.existsSync(path.join(projectRootDir, 'tests'));

  if (needToCareAboutTestsDir) {
    if (!fs.existsSync(testRootDir)) {
      console.error(
        'Error: ' +
          testRootDir +
          ' does not exist. Please create a tests/ directory in your project root!'
      );
      process.exit(1);
    }

    if (!fs.lstatSync(testRootDir).isDirectory()) {
      console.error(
        'Error: ' +
          testRootDir +
          ' exists, but it is not a directory. Please create a tests/ directory in your project root!'
      );
      process.exit(1);
    }
  }

  fs.mkdirpSync(generatedCodeDir);
  fs.mkdirpSync(generatedSrc);

  var testElmJson = {
    type: 'application',
    'source-directories': [], // these are added below
    'elm-version': '0.19.1',
    dependencies: {
      direct: {},
      indirect: {},
    },
    'test-dependencies': {
      direct: {},
      indirect: {},
    },
  };

  var nodeTestRunnerElmJsonPath = path.resolve(
    path.join(__dirname, '..', 'elm', 'elm.json')
  );
  var nodeTestRunnerElmJson = fs.readJsonSync(nodeTestRunnerElmJsonPath);
  // we want to use the version of elm-explorations/test that the user
  // specifies in their own test-dependencies. everything else needs to
  // be included for the test runner to compile.
  delete nodeTestRunnerElmJson.dependencies.direct['elm-explorations/test'];
  addDirectDependencies(
    nodeTestRunnerElmJson['dependencies']['direct'],
    isPackageProject,
    testElmJson
  );
  addIndirectDependencies(
    nodeTestRunnerElmJson['dependencies']['indirect'],
    testElmJson
  );

  if (isPackageProject) {
    addDirectDependencies(
      projectElmJson['dependencies'],
      isPackageProject,
      testElmJson
    );
    addDirectDependencies(
      projectElmJson['test-dependencies'],
      isPackageProject,
      testElmJson
    );
    // package projects don't explicitly list their transitive dependencies,
    // to we have to figure out what they are.  We write the elm.json that
    // we have so far, and run elm to see what it thinks is missing.
    fs.writeFileSync(
      path.join(generatedCodeDir, 'elm.json'),
      JSON.stringify(testElmJson, null, 4)
    );
    var missingDeps = askElmForMissingTransitiveDependencies(
      pathToElmBinary,
      generatedCodeDir
    );
    addIndirectDependencies(missingDeps, testElmJson);
  } else {
    addDirectDependencies(
      projectElmJson['dependencies']['direct'],
      isPackageProject,
      testElmJson
    );
    addIndirectDependencies(
      projectElmJson['dependencies']['indirect'],
      testElmJson
    );
    addDirectDependencies(
      projectElmJson['test-dependencies']['direct'],
      isPackageProject,
      testElmJson
    );
    addIndirectDependencies(
      projectElmJson['test-dependencies']['indirect'],
      testElmJson
    );
  }

  // Make all the source-directories absolute, and introduce a new one.
  var projectSourceDirs;
  if (isPackageProject) {
    projectSourceDirs = ['./src'];
  } else {
    projectSourceDirs = projectElmJson['source-directories'];
  }
  var sourceDirs /*: Array<string> */ = projectSourceDirs
    .map(function(src) {
      return path.resolve(path.join(projectRootDir, src));
    })
    .concat(shouldAddTestsDirAsSource ? [testRootDir] : []);

  testElmJson['source-directories'] = [
    // Include elm-stuff/generated-sources - since we'll be generating sources in there.
    generatedSrc,

    // NOTE: we must include node-test-runner's Elm source as a source-directory
    // instead of adding it as a dependency so that it can include port modules
    path.resolve(path.join(__dirname, '..', 'elm', 'src')),
  ]
  .concat(sourceDirs)
  .filter(
    // When running node-test-runner's own test suite, the node-test-runner/src folder
    // will get added twice: once because it's the source-directory of the packge being tested,
    // and once because elm-test will always add it.
    // To prevent elm from being confused, we need to remove the duplicate when this happens.
    function(value, index, self) {
      return self.indexOf(value) === index;
    }
  ).map(function(absolutePath) {
    // These all need to be relative paths. Otherwise, there's a bug in elm make
    // (as of 0.19.1-alpha4) where certain .elmi files wouldn't get generated.
    //
    // SSCCE: https://gist.github.com/rtfeldman/c0a068794b2e36d350c00357f458c50f
    //
    // Relative paths also have the nice benefit that if the user moves their
    // directory, this doesn't break.
    return path.relative(generatedCodeDir, absolutePath);
  });

  // Generate the new elm.json
  fs.writeFileSync(
    path.join(generatedCodeDir, 'elm.json'),
    JSON.stringify(testElmJson, null, 4)
  );

  return [generatedCodeDir, generatedSrc, sourceDirs];
}

function addDirectDependencies(
  deps /*: Object */,
  isPackageProject /*: boolean */,
  testElmJson /*: Object */
) {
  Object.keys(deps).forEach(function(name) {
    var version = deps[name];
    if (isPackageProject) {
      // Use the lowest version in the range.
      // NOTE: technically this doesn't work if someone does something weird like:
      //
      // "2.0.0 < v < 3.0.0"
      //
      // ...but we're choosing not to support that right now.
      version = version.split(' ')[0];
    }
    if (testElmJson['dependencies']['direct'].hasOwnProperty(name)) {
      var existingVersion = testElmJson['dependencies']['direct'][name];

      // If we have a clash, choose the higher of the two versions.
      // This may not work! It's entirely possible that the result won't
      // compile. We're going to try it and see what happens.
      version = Version.getHigherVersion(version, existingVersion);
    }
    testElmJson['dependencies']['direct'][name] = version;
  });
}

function addIndirectDependencies(deps, testElmJson /*: Object */) {
  Object.keys(deps).forEach(function(name) {
    if (testElmJson['dependencies']['direct'].hasOwnProperty(name)) {
      // already a normal dep
    } else {
      var version = deps[name];

      if (testElmJson['dependencies']['indirect'].hasOwnProperty(name)) {
        var existingVersion = testElmJson['dependencies']['indirect'][name];

        // If we have a clash, choose the higher of the two versions.
        // This may not work! It's entirely possible that the result won't
        // compile. We're going to try it and see what happens.
        version = Version.getHigherVersion(version, existingVersion);
      }

      testElmJson['dependencies']['indirect'][name] = version;
    }
  });
}
function askElmForMissingTransitiveDependencies(
  pathToElmBinary,
  pathToElmProject
) {
  var result = spawn.sync(pathToElmBinary, ['make', '--report=json'], {
    silent: true,
    env: process.env,
    cwd: pathToElmProject,
  });

  if (result.stderr == null) {
    console.error(
      'No output received from elm make when searching for indirect dependencies.'
    );
    process.exit(1);
  }

  // TODO: hopefully the next Elm 0.19 beta will print the JSON to stdout instead
  var output = result.stderr.toString();

  // TODO: hopefully the next Elm 0.19 beta will not have this message (only occurs on linux) mixed in with the JSON output
  output = output.replace(
    /^elm:.*no version information available \(required by elm\)\n/,
    ''
  );

  var report = JSON.parse(output);
  if (report.type === 'error' && report.title === 'NO INPUT') {
    // all transtive dependencies are listed already (elm is complaining that we didn't tell it which .elm file to build, which happens after the dependency validations)
    return {};
  } else if (
    report.type === 'error' &&
    report.title === 'MISSING DEPENDENCIES'
  ) {
    var missingDeps = {};
    // parse the missing dependencies and versions from the error report
    report.message[1].string.replace(/"([^"]*)": "([^"]*)"/g, function(
      _,
      name,
      version
    ) {
      missingDeps[name] = version;
    });
    return missingDeps;
  } else {
    console.error(
      "elm-test internal error: got an unexpected result from 'elm make' when validating transitive dependencies.  Please report this at https://github.com/rtfeldman/node-test-runner/issues"
    );
    process.exit(1);
    return {}; // This makes flow happy because it doesn't know process.exit will stop everything
  }
}

function generateMainModule(
  fuzz /*: number */,
  seed /*: number */,
  report /*: string */,
  testFilePaths /*: Array<string> */,
  tests /*: Array<Object> */,
  generatedSrc /*: string */,
  processes /*: number */
) {
  if (tests.length === 0) {
    const errorMessage =
      testFilePaths.length > 0
        ? 'I couldn\'t find any exposed values of type Test in files matching "' +
          testFilePaths.toString() +
          '"\n\nMaybe try running elm-test with no arguments?'
        : "I couldn't find any exposed values of type Test in any *.elm files in the tests/ directory of your project's root directory.\n\nTo generate some initial tests to get things going, run elm-test init";

    console.error(errorMessage);
    process.exit(1);
  }

  // Building things like:
  //
  // import MyTests
  //
  // MyTests.suite
  const imports = _.map(tests, function(test) {
    return 'import ' + test.name;
  });
  const testList = _.map(tests, function(mod) {
    return (
      '    Test.describe "' +
      mod.name +
      '" [' +
      _.map(mod.tests, function(test) {
        return mod.name + '.' + test;
      }).join(',\n    ') +
      ']'
    );
  });

  function sanitizedToString(str) {
    return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
  }

  function getReportCode() {
    switch (report) {
      case 'json':
        return 'JsonReport';
      case 'junit':
        return 'JUnitReport';
      default:
        if (supportsColor) {
          return '(ConsoleReport UseColor)';
        } else {
          return '(ConsoleReport Monochrome)';
        }
    }
  }

  if (isNaN(seed)) {
    seed = Math.floor(Math.random() * 407199254740991) + 1000;
  }

  var opts = {
    fuzz: isNaN(fuzz) ? 'Nothing' : 'Just ' + fuzz,
    seed: seed,
    report: getReportCode(),
    paths: testFilePaths.map(sanitizedToString).join(','),
  };

  var optsCode =
    '{ runs = ' +
    opts.fuzz +
    ', report = ' +
    opts.report +
    ', seed = ' +
    opts.seed +
    ', processes = ' +
    processes +
    ', paths = [' +
    opts.paths +
    ']}';

  const testFileBody = [
    imports.join('\n'),
    '',
    'import Test.Reporter.Reporter exposing (Report(..))',
    'import Console.Text exposing (UseColor(..))',
    'import Test.Runner.Node',
    'import Test',
    '',
    'main : Test.Runner.Node.TestProgram',
    'main =',
    '    [ ' + testList.join(', ') + ' ]',
    '        |> Test.concat',
    '        |> Test.Runner.Node.run ' + optsCode,
  ].join('\n');

  // Generate a filename that incorporates the hash of file contents.
  // This way, if you run e.g. `elm-test Foo.elm` and then `elm-test Bar.elm`
  // and then re-run `elm-test Foo.elm` we still have a cached `Main` for
  // `Foo.elm` (assuming none of its necessary imports have changed - and
  // why would they?) so we don't have to recompile it.
  const salt = Murmur.murmur3(testFileBody);
  const moduleName = 'Main' + salt;
  const mainPath = path.join(generatedSrc, 'Test', 'Generated');
  const mainFile = path.join(mainPath, moduleName + '.elm');
  // We'll be putting the generated Main in something like this:
  //
  // my-project-name/elm-stuff/generated-code/elm-community/elm-test/src/Test/Generated/Main123456.elm
  const testFileContents = [
    'module Test.Generated.' + moduleName + ' exposing (main)',
    testFileBody,
  ].join('\n\n');

  // Make sure src/Test/Generated/ exists so we can write the file there.
  fs.mkdirpSync(mainPath);

  // Always write the file, in order to update its timestamp. This is important,
  // because if we run `elm-make Main123456.elm` and that file's timestamp did
  // not change, elm-make will short-circuit and not recompile *anything* - even
  // if some of Main's dependencies (such as an individual test file) changed.
  fs.writeFileSync(mainFile, testFileContents);

  return mainFile;
}

module.exports = { prepareCompiledJsFile, generateElmJson, generateMainModule };
<|endoftext|>


<commit_before>import Immutable from 'immutable';

export const Events = {
  CHANGE: 'store:change',
  CHANGE_SELECTION: 'store:change-selection',
  LOADING_START: 'store:loading-start',
  LOADING_END: 'store:loading-end',
};

const defaultSequence = new Immutable.List('AAACGAAAACT'.split(''));
const defaultLabels = new Immutable.List([
  {
    name: 'Exon',
    color: '#334854',
    isActive: true,
  },
  {
    name: 'Primer',
    color: '#f9c535',
    isActive: true,
  },
  {
    name: 'SNP',
    color: '#e04462',
    isActive: true,
  },
]);

export default class Store {

  constructor(events) {
    this.events = events;
    this.reader = new FileReader();

    this.state = {
      sequence: defaultSequence,
      labels: defaultLabels,
      selection: new Immutable.OrderedSet(),
    };

    // file reader
    this.offset = 0;
    this.file = null;
    this.fileParts = [];
    this.reader.onload = this.onFileLoad.bind(this);
  }

  getState() {
    return this.state;
  }

  loadFromFile(file) {
    this.events.emit(Events.LOADING_START);

    // reset
    this.offset = 0;
    this.file = file;
    this.fileParts = [];

    this.loadFileChunk();
  }

  loadFileChunk() {
    const end = this.offset + (10 * 1024); // chunk size
    const blob = this.file.slice(this.offset, end);
    this.offset = end;

    this.reader.readAsText(blob);
  }

  onFileLoad(event) {
    if (null === event.target.error) {
      let parts = event.target.result.split('\n');

      if (/>/.test(parts[0])) {
        parts = parts.slice(1);
      }

      this.fileParts = this.fileParts.concat(parts);
    }

    if (this.offset >= this.file.size) {
      this.state.sequence = new Immutable.List(
        this.fileParts.join('').split('')
      );

      this.events.emit(Events.LOADING_END);
      this.events.emit(Events.CHANGE, this.state);

      return;
    }

    this.loadFileChunk();
  }

  addNewLabel(label) {
    this.state.labels = this.state.labels.push(label);

    this.events.emit(Events.CHANGE, this.state);
  }

  updateLabelAt(index, label) {
    this.state.labels = this.state.labels.update(index, () => (
      {
        name: label.name,
        color: label.color,
        isActive: true,
      }
    ));

    this.events.emit(Events.CHANGE, this.state);
  }

  removeLabelAt(index) {
    this.state.labels = this.state.labels.splice(index, 1);

    this.events.emit(Events.CHANGE, this.state);
  }

  toggleLabelAt(index) {
    this.state.labels = this.state.labels.update(index, (label) => (
      {
        name: label.name,
        color: label.color,
        isActive: !label.isActive,
      }
    ));

    this.events.emit(Events.CHANGE, this.state);
  }

  clearSelection() {
    this.state.selection = this.state.selection.clear();
  }

  updateSelection(selected) {
    let previousSelection = this.state.selection;

    if (2 <= previousSelection.size) {
      previousSelection = previousSelection.slice(1);
    }

    this.state.selection = previousSelection.has(selected) ?
      previousSelection.delete(selected) : previousSelection.add(selected);

    this.events.emit(Events.CHANGE_SELECTION, this.state.selection);
  }
}
<commit_msg>[Store] Group file reader stuff in a single object<commit_after>import Immutable from 'immutable';

export const Events = {
  CHANGE: 'store:change',
  CHANGE_SELECTION: 'store:change-selection',
  LOADING_START: 'store:loading-start',
  LOADING_END: 'store:loading-end',
};

const defaultSequence = new Immutable.List('AAACGAAAACT'.split(''));
const defaultLabels = new Immutable.List([
  {
    name: 'Exon',
    color: '#334854',
    isActive: true,
  },
  {
    name: 'Primer',
    color: '#f9c535',
    isActive: true,
  },
  {
    name: 'SNP',
    color: '#e04462',
    isActive: true,
  },
]);

export default class Store {

  constructor(events) {
    this.events = events;

    this.state = {
      sequence: defaultSequence,
      labels: defaultLabels,
      selection: new Immutable.OrderedSet(),
    };

    // file reader
    this.file = {
      reader: new FileReader(),
      offset: 0,
      source: null,
      chunks: [],
    };
    this.file.reader.onload = this.onFileLoad.bind(this);
  }

  getState() {
    return this.state;
  }

  loadFromFile(file) {
    this.events.emit(Events.LOADING_START);

    this.file.offset = 0;
    this.file.source = file;
    this.file.chunks = [];

    this.loadFileChunk();
  }

  loadFileChunk() {
    const end = this.file.offset + (10 * 1024); // chunk size
    const blob = this.file.source.slice(this.file.offset, end);

    this.file.offset = end;
    this.file.reader.readAsText(blob);
  }

  onFileLoad(event) {
    if (null === event.target.error) {
      let chunks = event.target.result.split('\n');

      if (/>/.test(chunks[0])) {
        chunks = chunks.slice(1);
      }

      this.file.chunks = this.file.chunks.concat(chunks);
    }

    if (this.file.offset >= this.file.source.size) {
      this.state.sequence = new Immutable.List(
        this.file.chunks.join('').split('')
      );

      this.events.emit(Events.LOADING_END);
      this.events.emit(Events.CHANGE, this.state);

      return;
    }

    this.loadFileChunk();
  }

  addNewLabel(label) {
    this.state.labels = this.state.labels.push(label);

    this.events.emit(Events.CHANGE, this.state);
  }

  updateLabelAt(index, label) {
    this.state.labels = this.state.labels.update(index, () => (
      {
        name: label.name,
        color: label.color,
        isActive: true,
      }
    ));

    this.events.emit(Events.CHANGE, this.state);
  }

  removeLabelAt(index) {
    this.state.labels = this.state.labels.splice(index, 1);

    this.events.emit(Events.CHANGE, this.state);
  }

  toggleLabelAt(index) {
    this.state.labels = this.state.labels.update(index, (label) => (
      {
        name: label.name,
        color: label.color,
        isActive: !label.isActive,
      }
    ));

    this.events.emit(Events.CHANGE, this.state);
  }

  clearSelection() {
    this.state.selection = this.state.selection.clear();
  }

  updateSelection(selected) {
    let previousSelection = this.state.selection;

    if (2 <= previousSelection.size) {
      previousSelection = previousSelection.slice(1);
    }

    this.state.selection = previousSelection.has(selected) ?
      previousSelection.delete(selected) : previousSelection.add(selected);

    this.events.emit(Events.CHANGE_SELECTION, this.state.selection);
  }
}
<|endoftext|>


<commit_before>'use strict';
import React from "react";
import ReactDOM from 'react-dom';
import jQuery from 'jquery';
import keyboard from 'virtual-keyboard/dist/js/jquery.keyboard.js';

export default class extends React.Component {
    constructor() {
        super();
        this.state = { value: "" };
        this.handleChange = this.handleChange.bind(this);
    }
    componentDidMount() {
        // Set Value to Input Element on Accept
        this.setState({ value: this.props.value });
        this.setState({ className: 'keyboard-wrapper' });

        this.props.options.accepted = function(event, keyboard, el) {
            this.handleChange('', el.value);
            if(typeof this.props.onAccepted == 'function') {
                this.props.onAccepted(el.value);
            }
            console.log('The content "' + el.value + '" was accepted');
        }.bind(this);

        // Set Class to visible
        this.props.options.visible = function function_name(event, keyboard, el) {
            console.log(keyboard);
            this.setState({ className: 'keyboard-wrapper open' });
        }.bind(this);

        this.props.options.hidden = function function_name(event, keyboard, el) {
            console.log(keyboard);
            this.setState({ className: 'keyboard-wrapper' });
        }.bind(this);

        // Set Value to Input Element on Change if prop set
        if (this.props.options.updateOnChange == true) {
            this.props.options.change = function(event, keyboard, el) {
                this.handleChange('', keyboard.preview.value);
                console.log('The content "' + el.value + '" was changed', el, keyboard.preview.value);
            }.bind(this);
        }

        //Add jQuery Keyboard to DOM Element
        jQuery(ReactDOM.findDOMNode(this.refs.keyboard)).keyboard(this.props.options);

        // Update while typing if usePreview is false
        if (this.props.options.usePreview === false) {
            jQuery(ReactDOM.findDOMNode(this.refs.keyboard)).on('keyboardChange', function(event, keyboard, el) {
                this.handleChange(null, keyboard.preview.value);
            }.bind(this));
        }
    }
    clear() {
        this.setState({ value: '' });
    }
    select() {
        this.refs.keyboard.select();
    }
    blur() {
        var keyboard = jQuery(ReactDOM.findDOMNode(this.refs.keyboard));
        if(keyboard && typeof keyboard.getkeyboard == 'function' && typeof keyboard.getkeyboard().close == 'function')
          keyboard.getkeyboard().close();
        this.refs.keyboard.blur();
    }
    handleChange(event, input) {
        if(!input && event.target && event.target.value)
          input = event.target.value;
        console.log("change", input, event, this);
        this.setState({ value: input });
        this.props.onChange(input);
    }
    render() {
    var { options, value, onChange, ...other } = this.props;
    if (this.props.options.type == 'textarea') {
         var element = ( <textarea ref="keyboard" value = { this.state.value } onChange = { this.handleChange } {...other}/> );
     }
     else {
        var element = ( <input ref="keyboard" value = { this.state.value } onChange = { this.handleChange } {...other} /> );
     }
        return (
 { element }
 );
    }
}
<commit_msg>Returns Validity Function to Parent Element<commit_after>'use strict';
import React from "react";
import ReactDOM from 'react-dom';
import jQuery from 'jquery';
import keyboard from 'virtual-keyboard/dist/js/jquery.keyboard.js';

export default class extends React.Component {
    constructor() {
        super();
        this.state = { value: "" };
        this.handleChange = this.handleChange.bind(this);
    }
    componentDidMount() {
        // Set Value to Input Element on Accept
        this.setState({ value: this.props.value });
        this.setState({ className: 'keyboard-wrapper' });

        this.props.options.accepted = function(event, keyboard, el) {
            this.handleChange('', el.value);
            if(typeof this.props.onAccepted == 'function') {
                this.props.onAccepted(el.value);
            }
            console.log('The content "' + el.value + '" was accepted');
        }.bind(this);

        // Set Class to visible
        this.props.options.visible = function function_name(event, keyboard, el) {
            console.log(keyboard);
            this.setState({ className: 'keyboard-wrapper open' });
        }.bind(this);

        this.props.options.hidden = function function_name(event, keyboard, el) {
            console.log(keyboard);
            this.setState({ className: 'keyboard-wrapper' });
        }.bind(this);

        // Set Value to Input Element on Change if prop set
        if (this.props.options.updateOnChange == true) {
            this.props.options.change = function(event, keyboard, el) {
                this.handleChange('', keyboard.preview.value);
                console.log('The content "' + el.value + '" was changed', el, keyboard.preview.value);
            }.bind(this);
        }

        //Add jQuery Keyboard to DOM Element
        jQuery(ReactDOM.findDOMNode(this.refs.keyboard)).keyboard(this.props.options);

        // Update while typing if usePreview is false
        if (this.props.options.usePreview === false) {
            jQuery(ReactDOM.findDOMNode(this.refs.keyboard)).on('keyboardChange', function(event, keyboard, el) {
                this.handleChange(null, keyboard.preview.value);
            }.bind(this));
        }
    }
    clear() {
        this.setState({ value: '' });
    }
    select() {
        this.refs.keyboard.select();
    }
    blur() {
        var keyboard = jQuery(ReactDOM.findDOMNode(this.refs.keyboard));
        if(keyboard && typeof keyboard.getkeyboard == 'function' && typeof keyboard.getkeyboard().close == 'function')
          keyboard.getkeyboard().close();
        this.refs.keyboard.blur();
    }
    checkValidity() {
        return this.refs.keyboard.checkValidity();
    }
    handleChange(event, input) {
        if(!input && event.target && event.target.value)
          input = event.target.value;
        console.log("change", input, event, this);
        this.setState({ value: input });
        this.props.onChange(input);
    }
    render() {
    var { options, value, validation, onChange, ...other } = this.props;
    if (this.props.options.type == 'textarea') {
         var element = ( <textarea ref="keyboard" value = { this.state.value } onChange = { this.handleChange } {...other}/> );
     }
     else {
        var element = ( <input ref="keyboard" value = { this.state.value } onChange = { this.handleChange } {...other} /> );
     }
        return (
 { element }
 );
    }
}
<|endoftext|>


<commit_before>/* @flow */
import type { GlobalState } from '../types';
import {
  EVENT_NEW_MESSAGE,
  EVENT_PRESENCE,
  EVENT_REACTION_ADD,
  EVENT_REACTION_REMOVE,
  EVENT_STREAM_ADD,
  EVENT_STREAM_REMOVE,
  EVENT_STREAM_UPDATE,
  EVENT_STREAM_OCCUPY,
  EVENT_SUBSCRIPTION_ADD,
  EVENT_SUBSCRIPTION_REMOVE,
  EVENT_SUBSCRIPTION_UPDATE,
  EVENT_SUBSCRIPTION_PEER_ADD,
  EVENT_SUBSCRIPTION_PEER_REMOVE,
  EVENT_TYPING_START,
  EVENT_TYPING_STOP,
  EVENT_UPDATE_MESSAGE,
  EVENT_UPDATE_MESSAGE_FLAGS,
  EVENT_USER_ADD,
  EVENT_USER_REMOVE,
  EVENT_USER_UPDATE,
  EVENT_MUTED_TOPICS,
  EVENT_REALM_EMOJI_UPDATE,
  EVENT_UPDATE_GLOBAL_NOTIFICATIONS_SETTINGS,
  EVENT_UPDATE_DISPLAY_SETTINGS,
  EVENT_REALM_FILTER_UPDATE,
} from '../actionConstants';

import { getUserById } from '../selectors';

const opToActionSubscription = {
  add: EVENT_SUBSCRIPTION_ADD,
  remove: EVENT_SUBSCRIPTION_REMOVE,
  update: EVENT_SUBSCRIPTION_UPDATE,
  peer_add: EVENT_SUBSCRIPTION_PEER_ADD,
  peer_remove: EVENT_SUBSCRIPTION_PEER_REMOVE,
};

const opToActionStream = {
  create: EVENT_STREAM_ADD,
  delete: EVENT_STREAM_REMOVE,
  update: EVENT_STREAM_UPDATE,
  occupy: EVENT_STREAM_OCCUPY,
};

const opToActionUser = {
  add: EVENT_USER_ADD,
  remove: EVENT_USER_REMOVE,
  update: EVENT_USER_UPDATE,
};

const opToActionReaction = {
  add: EVENT_REACTION_ADD,
  remove: EVENT_REACTION_REMOVE,
};

const opToActionTyping = {
  start: EVENT_TYPING_START,
  stop: EVENT_TYPING_STOP,
};

export default (state: GlobalState, event: Object) => {
  switch (event.type) {
    case 'message':
      return {
        type: EVENT_NEW_MESSAGE,
        message: event.message,
        ownEmail: state.accounts[0].email,
      };

    case 'update_message':
      return {
        ...event,
        type: EVENT_UPDATE_MESSAGE,
      };

    case 'subscription':
      return {
        ...event,
        type: opToActionSubscription[event.op],
        user: getUserById(state.users, event.user_id),
      };

    case 'realm_user':
      return {
        ...event,
        type: opToActionUser[event.op],
      };

    case 'realm_bot':
      break;

    case 'stream':
      return {
        ...event,
        type: opToActionStream[event.op],
      };
    case 'pointer':
      // TODO
      console.log(event); // eslint-disable-line
      break;

    case 'reaction':
      return {
        type: opToActionReaction[event.op],
        emoji: event.emoji_name,
        messageId: event.message_id,
        user: event.user,
      };

    case 'heartbeat':
      // ignore, no need to handle
      return null;

    case 'presence':
      return {
        ...event,
        type: EVENT_PRESENCE,
      };

    case 'update_message_flags':
      return {
        ...event,
        type: EVENT_UPDATE_MESSAGE_FLAGS,
      };

    case 'typing':
      return {
        ...event,
        ownEmail: state.accounts[0].email,
        type: opToActionTyping[event.op],
      };
    case 'muted_topics':
      return {
        type: EVENT_MUTED_TOPICS,
        ...event,
      };
    case 'realm_emoji':
      return {
        ...event,
        type: EVENT_REALM_EMOJI_UPDATE,
      };
    case 'realm_filters':
      return {
        ...event,
        type: EVENT_REALM_FILTER_UPDATE,
      };
    case 'update_global_notifications':
      return {
        ...event,
        type: EVENT_UPDATE_GLOBAL_NOTIFICATIONS_SETTINGS,
      };
    case 'update_display_settings':
      return {
        ...event,
        type: EVENT_UPDATE_DISPLAY_SETTINGS,
      };
    default:
  }

  // eslint-disable-next-line no-console
  console.warn('Unrecognized event: ', event.type);
  return null;
};
<commit_msg>fix: mute topics are not updated on events. (#959)<commit_after>/* @flow */
import type { GlobalState } from '../types';
import {
  EVENT_NEW_MESSAGE,
  EVENT_PRESENCE,
  EVENT_REACTION_ADD,
  EVENT_REACTION_REMOVE,
  EVENT_STREAM_ADD,
  EVENT_STREAM_REMOVE,
  EVENT_STREAM_UPDATE,
  EVENT_STREAM_OCCUPY,
  EVENT_SUBSCRIPTION_ADD,
  EVENT_SUBSCRIPTION_REMOVE,
  EVENT_SUBSCRIPTION_UPDATE,
  EVENT_SUBSCRIPTION_PEER_ADD,
  EVENT_SUBSCRIPTION_PEER_REMOVE,
  EVENT_TYPING_START,
  EVENT_TYPING_STOP,
  EVENT_UPDATE_MESSAGE,
  EVENT_UPDATE_MESSAGE_FLAGS,
  EVENT_USER_ADD,
  EVENT_USER_REMOVE,
  EVENT_USER_UPDATE,
  EVENT_MUTED_TOPICS,
  EVENT_REALM_EMOJI_UPDATE,
  EVENT_UPDATE_GLOBAL_NOTIFICATIONS_SETTINGS,
  EVENT_UPDATE_DISPLAY_SETTINGS,
  EVENT_REALM_FILTER_UPDATE,
} from '../actionConstants';

import { getUserById } from '../selectors';

const opToActionSubscription = {
  add: EVENT_SUBSCRIPTION_ADD,
  remove: EVENT_SUBSCRIPTION_REMOVE,
  update: EVENT_SUBSCRIPTION_UPDATE,
  peer_add: EVENT_SUBSCRIPTION_PEER_ADD,
  peer_remove: EVENT_SUBSCRIPTION_PEER_REMOVE,
};

const opToActionStream = {
  create: EVENT_STREAM_ADD,
  delete: EVENT_STREAM_REMOVE,
  update: EVENT_STREAM_UPDATE,
  occupy: EVENT_STREAM_OCCUPY,
};

const opToActionUser = {
  add: EVENT_USER_ADD,
  remove: EVENT_USER_REMOVE,
  update: EVENT_USER_UPDATE,
};

const opToActionReaction = {
  add: EVENT_REACTION_ADD,
  remove: EVENT_REACTION_REMOVE,
};

const opToActionTyping = {
  start: EVENT_TYPING_START,
  stop: EVENT_TYPING_STOP,
};

export default (state: GlobalState, event: Object) => {
  switch (event.type) {
    case 'message':
      return {
        type: EVENT_NEW_MESSAGE,
        message: event.message,
        ownEmail: state.accounts[0].email,
      };

    case 'update_message':
      return {
        ...event,
        type: EVENT_UPDATE_MESSAGE,
      };

    case 'subscription':
      return {
        ...event,
        type: opToActionSubscription[event.op],
        user: getUserById(state.users, event.user_id),
      };

    case 'realm_user':
      return {
        ...event,
        type: opToActionUser[event.op],
      };

    case 'realm_bot':
      break;

    case 'stream':
      return {
        ...event,
        type: opToActionStream[event.op],
      };
    case 'pointer':
      // TODO
      console.log(event); // eslint-disable-line
      break;

    case 'reaction':
      return {
        type: opToActionReaction[event.op],
        emoji: event.emoji_name,
        messageId: event.message_id,
        user: event.user,
      };

    case 'heartbeat':
      // ignore, no need to handle
      return null;

    case 'presence':
      return {
        ...event,
        type: EVENT_PRESENCE,
      };

    case 'update_message_flags':
      return {
        ...event,
        type: EVENT_UPDATE_MESSAGE_FLAGS,
      };

    case 'typing':
      return {
        ...event,
        ownEmail: state.accounts[0].email,
        type: opToActionTyping[event.op],
      };
    case 'muted_topics':
      return {
        ...event,
        type: EVENT_MUTED_TOPICS,
      };
    case 'realm_emoji':
      return {
        ...event,
        type: EVENT_REALM_EMOJI_UPDATE,
      };
    case 'realm_filters':
      return {
        ...event,
        type: EVENT_REALM_FILTER_UPDATE,
      };
    case 'update_global_notifications':
      return {
        ...event,
        type: EVENT_UPDATE_GLOBAL_NOTIFICATIONS_SETTINGS,
      };
    case 'update_display_settings':
      return {
        ...event,
        type: EVENT_UPDATE_DISPLAY_SETTINGS,
      };
    default:
  }

  // eslint-disable-next-line no-console
  console.warn('Unrecognized event: ', event.type);
  return null;
};
<|endoftext|>


<commit_before>/*global window*/
var utils = require('./utils');
var extend = utils.extend;
var duplicateText = require('./duplicateText');

function MagicPen(options) {
    if (!(this instanceof MagicPen)) {
        return new MagicPen(options);
    }

    options = options || {};

    var indentationWidth = 'indentationWidth' in options ?
        options.indentationWidth : 2;
    this.indentationWidth = Math.max(indentationWidth, 0);

    this.indentationLevel = 0;
    this.output = [];
    this.styles = {};
}

if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') {
    MagicPen.defaultFormat = 'html'; // Browser
} else if (require('supports-color')) {
    MagicPen.defaultFormat = 'ansi'; // colored console
} else {
    MagicPen.defaultFormat = 'text'; // Plain text
}

MagicPen.prototype.newline = MagicPen.prototype.nl = function (count) {
    if (typeof count === 'undefined') {
        count = 1;
    }

    if (count === 0) {
        return this;
    }

    this.output[0] = this.output[0] || [];

    for (var i = 0; i < count; i += 1) {
        this.output.push([]);
    }
    return this;
};

MagicPen.serializers = {
    text: require('./TextSerializer'),
    html: require('./HtmlSerializer'),
    ansi: require('./AnsiSerializer')
};

function hasSameTextStyling(a, b) {
    if (!a || !b || a.style !== 'text' || b.style !== 'text') {
        return false;
    }

    return utils.arrayEquals(Array.prototype.slice.call(a.args, 1),
                             Array.prototype.slice.call(b.args, 1));
}

function normalizeLine(line) {
    if (line.length === 0) {
        return line;
    }

    var result = [line[0]];
    for (var i = 1; i < line.length; i += 1) {
        var lastEntry = result[result.length - 1];
        var entry = line[i];
        if (entry.style === 'text' && entry.args[0] === '') {
            continue;
        }

        if (hasSameTextStyling(lastEntry, entry)) {
            result[result.length - 1] = {
                style: entry.style,
                args: [lastEntry.args[0] + entry.args[0]].concat(entry.args.slice(1))
            };
        } else {
            result.push(entry);
        }
    }

    return result;
}

MagicPen.prototype.write = function () {
    if (typeof arguments[0] === 'string') {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.write({ style: arguments[0], args: args });
    } else {
        var options = arguments[0];
        if (this.styles[options.style]) {
            this.styles[options.style].apply(this, options.args);
            return this;
        }
        this.output[0] = this.output[0] || [];
        var lastLine = this.output[this.output.length - 1];
        var lastEntry = lastLine[lastLine.length - 1];
        if (hasSameTextStyling(lastEntry, options)) {
            options.args[0] = lastEntry.args[0] + options.args[0];
            lastLine[lastLine.length - 1] = options;
        } else {
            lastLine.push(options);
        }

        return this;
    }
};

MagicPen.prototype.indentLines = function () {
    this.indentationLevel += 1;
    return this;
};

MagicPen.prototype.indent = MagicPen.prototype.i = function () {
    for (var i = 0; i < this.indentationLevel; i += 1) {
        this.space(this.indentationWidth);
    }
    return this;
};

MagicPen.prototype.outdentLines = function () {
    this.indentationLevel = Math.max(0, this.indentationLevel - 1);
    return this;
};

MagicPen.prototype.addStyle = function (style, handler, allowRedefinition) {
    if (this[style] && !allowRedefinition) {
        throw new Error('"' + style + '" style is already defined, set 3rd arg (allowRedefinition) to true to define it anyway');
    }
    this.styles[style] = handler;
    this[style] = function () {
        handler.apply(this, arguments);
        return this;
    };
    return this;
};

MagicPen.prototype.toString = function (format) {
    format = format || 'text';
    if (format === 'auto') {
        format = MagicPen.defaultFormat;
    }
    var serializer = new MagicPen.serializers[format]();
    return serializer.serialize(this.output);
};

MagicPen.prototype.text = function (content) {
    if (content === '') {
        return this;
    }

    var args = Array.prototype.slice.call(arguments, 0);
    content = String(content);
    if (content.indexOf('\n') !== -1) {
        args = args.slice(1);
        var lines = content.split(/\n/);
        lines.forEach(function (lineContent, index) {
            this.write({ style: 'text', args: [lineContent].concat(args) });
            if (index < lines.length - 1) {
                this.nl();
            }
        }, this);
        return this;
    } else {
        return this.write({ style: 'text', args: args });
    }
};

MagicPen.prototype.removeFormatting = function () {
    var result = this.clone();
    this.output.forEach(function (line, index) {
        result.output[index] = normalizeLine(line.map(function (outputEntry) {
            return outputEntry.style === 'text' ?
                { style: 'text', args: [outputEntry.args[0]] } :
                outputEntry;
        }));
    });
    result.indentationLevel = this.indentationLevel;
    return result;
};

MagicPen.prototype.getContentFromArguments = function (args) {
    var clone;
    if (args[0].isMagicPen) {
        return args[0];
    } else if (typeof args[0] === 'function') {
        clone = this.clone();
        args[0].call(clone, clone);
        return clone;
    } else if (typeof args[0] === 'string') {
        clone = this.clone();
        clone[args[0]].apply(clone, Array.prototype.slice.call(args, 1));
        return clone;
    } else {
        throw new Error('Requires the arguments to be:\n' +
                        'a pen or\n' +
                        'a callback append content to a penor\n' +
                        'a style and arguments for that style');
    }
};

MagicPen.prototype.block = function () {
    var pen = this.getContentFromArguments(arguments);

    var blockOutput = pen.output.map(function (line) {
        return [].concat(line);
    });
    return this.write('block', blockOutput);
};

MagicPen.prototype.append = function () {
    var pen = this.getContentFromArguments(arguments);

    if (pen.output.length === 0) {
        return this;
    }

    this.output[0] = this.output[0] || [];
    var lastLine = this.output[this.output.length - 1];
    Array.prototype.push.apply(lastLine, pen.output[0]);
    this.output[this.output.length - 1] = normalizeLine(lastLine);

    this.output.push.apply(this.output, pen.output.slice(1));

    return this;
};

MagicPen.prototype.prependLinesWith = function () {
    if (this.output.length === 0) {
        return this;
    }

    var pen = this.getContentFromArguments(arguments);

    if (pen.output.length === 0) {
        return this;
    }

    if (pen.output.length > 1) {
        throw new Error('PrependLinesWith only supports a pen with single line content');
    }

    var outputToPrepend = [].concat(pen.output[0]);

    this.output = this.output.map(function (line) {
        return normalizeLine(outputToPrepend.concat(line));
    });

    return this;
};

MagicPen.prototype.space = MagicPen.prototype.sp = function (count) {
    if (count === 0) {
        return this;
    }

    if (typeof count === 'undefined') {
        count = 1;
    }

    this.text(duplicateText(' ', count));
    return this;
};

[
    'bold', 'dim', 'italic', 'underline', 'inverse', 'hidden',
    'strikeThrough', 'black', 'red', 'green', 'yellow', 'blue',
    'magenta', 'cyan', 'white', 'gray', 'bgBlack', 'bgRed',
    'bgGreen', 'bgYellow', 'bgBlue', 'bgMagenta', 'bgCyan',
    'bgWhite'
].forEach(function (textStyle) {
    MagicPen.prototype[textStyle] = MagicPen.prototype[textStyle.toLowerCase()] = function (content) {
        return this.text.call(this, content, textStyle);
    };
});

MagicPen.prototype.clone = function () {
    function MagicPenClone() {}
    MagicPenClone.prototype = this;
    var clonedPen = new MagicPenClone();
    clonedPen.styles = extend({}, this.styles);
    clonedPen.indentationLevel = 0;
    clonedPen.output = [];
    return clonedPen;
};

MagicPen.prototype.isMagicPen = true;

MagicPen.prototype.size = function () {
    return utils.calculateSize(this.output);
};

MagicPen.prototype.installPlugin = function (plugin) {
    if (typeof plugin !== 'function') {
        throw new Error('Expected first argument given to installPlugin to be a function');
    }

    plugin(this);
    return this;
};

function replaceText(output, outputArray, regexp, cb) {
    var replacedOutput = output;
    outputArray.forEach(function (line, i) {
        if (0 < i) {
            replacedOutput.nl();
        }

        line.forEach(function (outputEntry, j) {
            if (outputEntry.style === 'block') {
                return replacedOutput.output[replacedOutput.output.length - 1].push({
                    style: 'block',
                    args: [replaceText(output.clone(), outputEntry.args[0], regexp, cb)]
                });
            } else if (outputEntry.style !== 'text') {
                return replacedOutput.output[replacedOutput.output.length - 1].push(outputEntry);
            }

            if (regexp.global) {
                regexp.lastIndex = 0;
            }
            var styles = outputEntry.args.slice(1);
            var m;
            var first = true;
            var lastIndex = 0;
            var text = outputEntry.args[0];
            while ((m = regexp.exec(text)) !== null && (regexp.global || first)) {
                if (lastIndex < m.index) {
                    replacedOutput.text.apply(replacedOutput, [text.substring(lastIndex, m.index)].concat(styles));
                }

                cb.apply(replacedOutput, [styles].concat(m));
                first = false;
                lastIndex = m.index + m[0].length;
            }

            if (lastIndex === 0) {
                var lastLine;
                if (replacedOutput.output.length === 0) {
                    lastLine = replacedOutput.output[0] = [];
                } else {
                    lastLine = replacedOutput.output[replacedOutput.output.length - 1];
                }

                lastLine.push(outputEntry);
            } else if (lastIndex < text.length) {
                replacedOutput.text.apply(replacedOutput, [text.substring(lastIndex, text.length)].concat(styles));
            }
        }, this);
    }, this);

    return replacedOutput.output.map(normalizeLine);
}

MagicPen.prototype.replaceText = function (regexp, cb) {
    if (this.output.length === 0) {
        return this;
    }

    if (typeof cb === 'string') {
        var text = cb;
        cb = function (styles, _) {
            var args = [text].concat(styles);
            this.text.apply(this, args);
        };
    }

    if (arguments.length === 1) {
        cb = regexp;
        regexp = /.*/;
    }

    this.output = replaceText(this.clone(), this.output, regexp, cb);

    return this;
};

module.exports = MagicPen;
<commit_msg>Make an empty magicpen contain the output [[]]<commit_after>/*global window*/
var utils = require('./utils');
var extend = utils.extend;
var duplicateText = require('./duplicateText');

function MagicPen(options) {
    if (!(this instanceof MagicPen)) {
        return new MagicPen(options);
    }

    options = options || {};

    var indentationWidth = 'indentationWidth' in options ?
        options.indentationWidth : 2;
    this.indentationWidth = Math.max(indentationWidth, 0);

    this.indentationLevel = 0;
    this.output = [[]];
    this.styles = {};
}

if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') {
    MagicPen.defaultFormat = 'html'; // Browser
} else if (require('supports-color')) {
    MagicPen.defaultFormat = 'ansi'; // colored console
} else {
    MagicPen.defaultFormat = 'text'; // Plain text
}

MagicPen.prototype.newline = MagicPen.prototype.nl = function (count) {
    if (typeof count === 'undefined') {
        count = 1;
    }

    if (count === 0) {
        return this;
    }

    for (var i = 0; i < count; i += 1) {
        this.output.push([]);
    }
    return this;
};

MagicPen.serializers = {
    text: require('./TextSerializer'),
    html: require('./HtmlSerializer'),
    ansi: require('./AnsiSerializer')
};

function hasSameTextStyling(a, b) {
    if (!a || !b || a.style !== 'text' || b.style !== 'text') {
        return false;
    }

    return utils.arrayEquals(Array.prototype.slice.call(a.args, 1),
                             Array.prototype.slice.call(b.args, 1));
}

function normalizeLine(line) {
    if (line.length === 0) {
        return line;
    }

    var result = [line[0]];
    for (var i = 1; i < line.length; i += 1) {
        var lastEntry = result[result.length - 1];
        var entry = line[i];
        if (entry.style === 'text' && entry.args[0] === '') {
            continue;
        }

        if (hasSameTextStyling(lastEntry, entry)) {
            result[result.length - 1] = {
                style: entry.style,
                args: [lastEntry.args[0] + entry.args[0]].concat(entry.args.slice(1))
            };
        } else {
            result.push(entry);
        }
    }

    return result;
}

MagicPen.prototype.write = function () {
    if (typeof arguments[0] === 'string') {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.write({ style: arguments[0], args: args });
    } else {
        var options = arguments[0];
        if (this.styles[options.style]) {
            this.styles[options.style].apply(this, options.args);
            return this;
        }
        var lastLine = this.output[this.output.length - 1];
        var lastEntry = lastLine[lastLine.length - 1];
        if (hasSameTextStyling(lastEntry, options)) {
            options.args[0] = lastEntry.args[0] + options.args[0];
            lastLine[lastLine.length - 1] = options;
        } else {
            lastLine.push(options);
        }

        return this;
    }
};

MagicPen.prototype.indentLines = function () {
    this.indentationLevel += 1;
    return this;
};

MagicPen.prototype.indent = MagicPen.prototype.i = function () {
    for (var i = 0; i < this.indentationLevel; i += 1) {
        this.space(this.indentationWidth);
    }
    return this;
};

MagicPen.prototype.outdentLines = function () {
    this.indentationLevel = Math.max(0, this.indentationLevel - 1);
    return this;
};

MagicPen.prototype.addStyle = function (style, handler, allowRedefinition) {
    if (this[style] && !allowRedefinition) {
        throw new Error('"' + style + '" style is already defined, set 3rd arg (allowRedefinition) to true to define it anyway');
    }
    this.styles[style] = handler;
    this[style] = function () {
        handler.apply(this, arguments);
        return this;
    };
    return this;
};

MagicPen.prototype.toString = function (format) {
    format = format || 'text';
    if (format === 'auto') {
        format = MagicPen.defaultFormat;
    }
    var serializer = new MagicPen.serializers[format]();
    return serializer.serialize(this.output);
};

MagicPen.prototype.text = function (content) {
    if (content === '') {
        return this;
    }

    var args = Array.prototype.slice.call(arguments, 0);
    content = String(content);
    if (content.indexOf('\n') !== -1) {
        args = args.slice(1);
        var lines = content.split(/\n/);
        lines.forEach(function (lineContent, index) {
            this.write({ style: 'text', args: [lineContent].concat(args) });
            if (index < lines.length - 1) {
                this.nl();
            }
        }, this);
        return this;
    } else {
        return this.write({ style: 'text', args: args });
    }
};

MagicPen.prototype.removeFormatting = function () {
    var result = this.clone();
    this.output.forEach(function (line, index) {
        result.output[index] = normalizeLine(line.map(function (outputEntry) {
            return outputEntry.style === 'text' ?
                { style: 'text', args: [outputEntry.args[0]] } :
                outputEntry;
        }));
    });
    result.indentationLevel = this.indentationLevel;
    return result;
};

MagicPen.prototype.getContentFromArguments = function (args) {
    var clone;
    if (args[0].isMagicPen) {
        return args[0];
    } else if (typeof args[0] === 'function') {
        clone = this.clone();
        args[0].call(clone, clone);
        return clone;
    } else if (typeof args[0] === 'string') {
        clone = this.clone();
        clone[args[0]].apply(clone, Array.prototype.slice.call(args, 1));
        return clone;
    } else {
        throw new Error('Requires the arguments to be:\n' +
                        'a pen or\n' +
                        'a callback append content to a penor\n' +
                        'a style and arguments for that style');
    }
};

MagicPen.prototype.block = function () {
    var pen = this.getContentFromArguments(arguments);

    var blockOutput = pen.output.map(function (line) {
        return [].concat(line);
    });
    return this.write('block', blockOutput);
};

MagicPen.prototype.append = function () {
    var pen = this.getContentFromArguments(arguments);

    if (pen.isEmpty()) {
        return this;
    }

    var lastLine = this.output[this.output.length - 1];
    Array.prototype.push.apply(lastLine, pen.output[0]);
    this.output[this.output.length - 1] = normalizeLine(lastLine);

    this.output.push.apply(this.output, pen.output.slice(1));

    return this;
};

MagicPen.prototype.prependLinesWith = function () {
    var pen = this.getContentFromArguments(arguments);

    if (pen.isEmpty()) {
        return this;
    }

    if (pen.output.length > 1) {
        throw new Error('PrependLinesWith only supports a pen with single line content');
    }

    var outputToPrepend = [].concat(pen.output[0]);

    this.output = this.output.map(function (line) {
        return normalizeLine(outputToPrepend.concat(line));
    });

    return this;
};

MagicPen.prototype.space = MagicPen.prototype.sp = function (count) {
    if (count === 0) {
        return this;
    }

    if (typeof count === 'undefined') {
        count = 1;
    }

    this.text(duplicateText(' ', count));
    return this;
};

[
    'bold', 'dim', 'italic', 'underline', 'inverse', 'hidden',
    'strikeThrough', 'black', 'red', 'green', 'yellow', 'blue',
    'magenta', 'cyan', 'white', 'gray', 'bgBlack', 'bgRed',
    'bgGreen', 'bgYellow', 'bgBlue', 'bgMagenta', 'bgCyan',
    'bgWhite'
].forEach(function (textStyle) {
    MagicPen.prototype[textStyle] = MagicPen.prototype[textStyle.toLowerCase()] = function (content) {
        return this.text.call(this, content, textStyle);
    };
});

MagicPen.prototype.clone = function () {
    function MagicPenClone() {}
    MagicPenClone.prototype = this;
    var clonedPen = new MagicPenClone();
    clonedPen.styles = extend({}, this.styles);
    clonedPen.indentationLevel = 0;
    clonedPen.output = [[]];
    return clonedPen;
};

MagicPen.prototype.isMagicPen = true;

MagicPen.prototype.size = function () {
    return utils.calculateSize(this.output);
};

MagicPen.prototype.installPlugin = function (plugin) {
    if (typeof plugin !== 'function') {
        throw new Error('Expected first argument given to installPlugin to be a function');
    }

    plugin(this);
    return this;
};

function replaceText(output, outputArray, regexp, cb) {
    var replacedOutput = output;
    outputArray.forEach(function (line, i) {
        if (0 < i) {
            replacedOutput.nl();
        }

        line.forEach(function (outputEntry, j) {
            if (outputEntry.style === 'block') {
                return replacedOutput.output[replacedOutput.output.length - 1].push({
                    style: 'block',
                    args: [replaceText(output.clone(), outputEntry.args[0], regexp, cb)]
                });
            } else if (outputEntry.style !== 'text') {
                return replacedOutput.output[replacedOutput.output.length - 1].push(outputEntry);
            }

            if (regexp.global) {
                regexp.lastIndex = 0;
            }
            var styles = outputEntry.args.slice(1);
            var m;
            var first = true;
            var lastIndex = 0;
            var text = outputEntry.args[0];
            while ((m = regexp.exec(text)) !== null && (regexp.global || first)) {
                if (lastIndex < m.index) {
                    replacedOutput.text.apply(replacedOutput, [text.substring(lastIndex, m.index)].concat(styles));
                }

                cb.apply(replacedOutput, [styles].concat(m));
                first = false;
                lastIndex = m.index + m[0].length;
            }

            if (lastIndex === 0) {
                var lastLine;
                if (replacedOutput.output.length === 0) {
                    lastLine = replacedOutput.output[0] = [];
                } else {
                    lastLine = replacedOutput.output[replacedOutput.output.length - 1];
                }

                lastLine.push(outputEntry);
            } else if (lastIndex < text.length) {
                replacedOutput.text.apply(replacedOutput, [text.substring(lastIndex, text.length)].concat(styles));
            }
        }, this);
    }, this);

    return replacedOutput.output.map(normalizeLine);
}

MagicPen.prototype.isEmpty = function () {
    return this.output.length === 1 && this.output[0].length === 0;
};

MagicPen.prototype.replaceText = function (regexp, cb) {
    if (this.isEmpty()) {
        return this;
    }

    if (typeof cb === 'string') {
        var text = cb;
        cb = function (styles, _) {
            var args = [text].concat(styles);
            this.text.apply(this, args);
        };
    }

    if (arguments.length === 1) {
        cb = regexp;
        regexp = /.*/;
    }

    this.output = replaceText(this.clone(), this.output, regexp, cb);

    return this;
};

module.exports = MagicPen;
<|endoftext|>


<commit_before>'use strict';

class Input {

    constructor() {
        this._keyState = new Map();
        this._keyState.set('left', 'up');
        this._keyState.set('up', 'up');
        this._keyState.set('down', 'up');
        this._keyState.set('enter', 'up');
        this._keyState.set('other', 'up');
    }

    start() {
        window.addEventListener('keydown', (e) => {
            this._eventToState(e, 'down');
        });

        window.addEventListener('keyup', (e) => {
            this._eventToState(e, 'up');
        })
    }

    // TODO: Maybe a reset() method?

    /**
     * Return if given key is 'down'.
     */
    isDown(key) {
        return this._keyState.get(key) === 'down';
    }

    /**
     * Return if given key is 'down'. Also sets the key from 'down' to 'handling'.
     */
    isDownAndUnhandled(key) {
        if (this.isDown(key)) {
            this._keyState.set(key, 'handling');
            return true;
        }
    }

    /**
     * Return if any arrow key is 'down'.
     */
    isArrowKeyDown() {
        return (this._keyState.get('up') === 'down' ||
                this._keyState.get('down') === 'down' ||
                this._keyState.get('left') === 'down' ||
                this._keyState.get('right') === 'down');
    }

    /**
     * Returns if any key is 'down'. Also set all 'down' keys to 'handling'.
     */
    isAnyKeyDownAndUnhandled() {
        let anyKeyDown = false;

        for (let [key, value] of this._keyState) {
            if (value === 'down') {
                this._keyState.set(key, 'handling');
                anyKeyDown = true;
            }
        }

        return anyKeyDown;
    }

    _eventToState(e, state) {
        switch (e.keyCode) {
            case 37: // left
                this._setState('left', state);
                break;
            case 38: // up
                this._setState('up', state);
                break;
            case 39: // right
                this._setState('right', state);
                break;
            case 40: // down
                this._setState('down', state);
                break;
            case 32: // space   (reroutes to enter)
            case 80: // 'p'     (reroutes to enter)
            case 27: // esc     (reroutes to enter)
            case 13: // enter key
                this._setState('enter', state);
                break;

            // ignore certain keys
            case 18:    // alt
            case 224:   // apple command (firefox)
            case 17:    // apple command (opera)
            case 91:    // apple command, left (safari/chrome)
            case 93:    // apple command, right (safari/chrome)
                break;

            // prevent losing focus via tab
            case 9:
                e.preventDefault();
                break;

            // all other keys
            default:
                this._setState('other', state);
                break;
        }
    };

    _setState(key, state) {
        // always set 'up'
        if (state === 'up') {
            this._keyState.set(key, 'up');

        // only set 'down' if it is not already handled
        } else if (state === 'down') {
            if (this._keyState.get(key) !== 'handling') {
                this._keyState.set(key, 'down');
            }
        }
    }
}

module.exports = Input;<commit_msg>Fix arrow keys to not scroll if window is small<commit_after>'use strict';

class Input {

    constructor() {
        this._keyState = new Map();
        this._keyState.set('left', 'up');
        this._keyState.set('up', 'up');
        this._keyState.set('down', 'up');
        this._keyState.set('enter', 'up');
        this._keyState.set('other', 'up');
    }

    start() {
        window.addEventListener('keydown', (e) => {
            this._eventToState(e, 'down');
        });

        window.addEventListener('keyup', (e) => {
            this._eventToState(e, 'up');
        })
    }

    // TODO: Maybe a reset() method?

    /**
     * Return if given key is 'down'.
     */
    isDown(key) {
        return this._keyState.get(key) === 'down';
    }

    /**
     * Return if given key is 'down'. Also sets the key from 'down' to 'handling'.
     */
    isDownAndUnhandled(key) {
        if (this.isDown(key)) {
            this._keyState.set(key, 'handling');
            return true;
        }
    }

    /**
     * Return if any arrow key is 'down'.
     */
    isArrowKeyDown() {
        return (this._keyState.get('up') === 'down' ||
                this._keyState.get('down') === 'down' ||
                this._keyState.get('left') === 'down' ||
                this._keyState.get('right') === 'down');
    }

    /**
     * Returns if any key is 'down'. Also set all 'down' keys to 'handling'.
     */
    isAnyKeyDownAndUnhandled() {
        let anyKeyDown = false;

        for (let [key, value] of this._keyState) {
            if (value === 'down') {
                this._keyState.set(key, 'handling');
                anyKeyDown = true;
            }
        }

        return anyKeyDown;
    }

    _eventToState(e, state) {
        switch (e.keyCode) {
            case 37: // left
                this._setState('left', state);
                e.preventDefault();
                break;
            case 38: // up
                this._setState('up', state);
                e.preventDefault();
                break;
            case 39: // right
                this._setState('right', state);
                e.preventDefault();
                break;
            case 40: // down
                this._setState('down', state);
                e.preventDefault();
                break;
            case 32: // space   (reroutes to enter)
            case 80: // 'p'     (reroutes to enter)
            case 27: // esc     (reroutes to enter)
            case 13: // enter key
                this._setState('enter', state);
                e.preventDefault();
                break;

            // ignore certain keys
            case 18:    // alt
            case 224:   // apple command (firefox)
            case 17:    // apple command (opera)
            case 91:    // apple command, left (safari/chrome)
            case 93:    // apple command, right (safari/chrome)
                break;

            // prevent losing focus via tab
            case 9:
                e.preventDefault();
                break;

            // all other keys
            default:
                this._setState('other', state);
                break;
        }
    };

    _setState(key, state) {
        // always set 'up'
        if (state === 'up') {
            this._keyState.set(key, 'up');

        // only set 'down' if it is not already handled
        } else if (state === 'down') {
            if (this._keyState.get(key) !== 'handling') {
                this._keyState.set(key, 'down');
            }
        }
    }
}

module.exports = Input;<|endoftext|>


<commit_before>// --- vdsm-screen -------------------------------------------------------

function loadVdsmConf(inform) {
    var editor = $("#editor-vdsm-conf");

    cockpit.file(VDSM_CONF_FILENAME).read().done(function (content, tag) {
        editor.val(content);
        debugMsg("Content of vdsm.conf loaded: " + content);
        if (inform) {
            writeVdsmConfMsg("Loaded", true);
        }
    }).fail(function (error) {
        printError("Error reading vdsm.conf: " + error)
    });
}

function reloadVdsmConf() {
    if (confirm("Content of vdsm.conf will be reloaded, unsaved changes will be lost.\n\nPlease confirm.")) {
        loadVdsmConf(true);
    }
}

function saveVdsmConf() {
    if (confirm("Content of vdsm.conf file will be replaced.\n\nPlease confirm.")) {
        var editor = $("#editor-vdsm-conf");
        var content = editor.val();

        cockpit.file(VDSM_CONF_FILENAME).replace(content).done(function (tag) {
            debugMsg("Content of vdsm.conf replaced.");
            writeVdsmConfMsg("Saved", true);
        }).fail(function (error) {
            printError("Error writing vdsm.conf: " + error);
        });
    }
}

function writeVdsmConfMsg(text, autoclear) {
    var msg = $("#editor-vds-conf-msg");
    msg.html(text);
    debugMsg("writeVdsmConfMsg: " + text);
    if (autoclear) {
        setTimeout(clearVdsmConfMsg, AUTO_CLEAR_MSG_DELAY)
    }
}

function clearVdsmConfMsg() {
    var msg = $("#editor-vds-conf-msg");
    msg.html("");
}
<commit_msg>Less logging in vdsm.conf edit<commit_after>// --- vdsm-screen -------------------------------------------------------

function loadVdsmConf(inform) {
    var editor = $("#editor-vdsm-conf");

    cockpit.file(VDSM_CONF_FILENAME).read().done(function (content, tag) {
        editor.val(content);
//        debugMsg("Content of vdsm.conf loaded: " + content);
        if (inform) {
            writeVdsmConfMsg("Loaded", true);
        }
    }).fail(function (error) {
        printError("Error reading vdsm.conf: " + error)
    });
}

function reloadVdsmConf() {
    if (confirm("Content of vdsm.conf will be reloaded, unsaved changes will be lost.\n\nPlease confirm.")) {
        loadVdsmConf(true);
    }
}

function saveVdsmConf() {
    if (confirm("Content of vdsm.conf file will be replaced.\n\nPlease confirm.")) {
        var editor = $("#editor-vdsm-conf");
        var content = editor.val();

        cockpit.file(VDSM_CONF_FILENAME).replace(content).done(function (tag) {
            debugMsg("Content of vdsm.conf replaced.");
            writeVdsmConfMsg("Saved", true);
        }).fail(function (error) {
            printError("Error writing vdsm.conf: " + error);
        });
    }
}

function writeVdsmConfMsg(text, autoclear) {
    var msg = $("#editor-vds-conf-msg");
    msg.html(text);
    debugMsg("writeVdsmConfMsg: " + text);
    if (autoclear) {
        setTimeout(clearVdsmConfMsg, AUTO_CLEAR_MSG_DELAY)
    }
}

function clearVdsmConfMsg() {
    var msg = $("#editor-vds-conf-msg");
    msg.html("");
}
<|endoftext|>


<commit_before>'use strict'

global.ECONOMY_CRASHED = 0
global.ECONOMY_FALTERING = 1
global.ECONOMY_DEVELOPING = 2
global.ECONOMY_STABLE = 3
global.ECONOMY_SURPLUS = 4
global.ECONOMY_BURSTING = 5

const economySettings = {
  'EXPAND_FROM': ECONOMY_DEVELOPING,
  'WALLBUILDERS': ECONOMY_DEVELOPING,
  'BUILD_STRUCTURES': ECONOMY_DEVELOPING,

  'EXTRACT_MINERALS': ECONOMY_STABLE,
  'UPGRADE_CONTROLLERS': ECONOMY_STABLE,

  'EXTRA_UPGRADERS': ECONOMY_SURPLUS,
  'EXTRA_WALLBUILDERS': ECONOMY_SURPLUS,

  'MORE_EXTRA_UPGRADERS': ECONOMY_BURSTING
}

Room.prototype.isEconomyCapable = function (key) {
  if (!economySettings[key]) {
    return false
  }
  return this.getEconomyLevel() >= economySettings[key]
}

Room.prototype.getEconomyLevel = function () {
  if (this.getPracticalRoomLevel() < 4) {
    return ECONOMY_STABLE
  }

  const desiredBuffer = this.getDesiredEnergyBuffer()
  const energy = this.getEnergyAmount()

  if (energy < 15000) {
    return ECONOMY_CRASHED
  }

  // When fully developed between 15000 and 280000
  if (energy < (desiredBuffer - 20000)) {
    return ECONOMY_FALTERING
  }

  // When fully developed between 280000 and 300000
  if (energy < (desiredBuffer)) {
    return ECONOMY_DEVELOPING
  }

  // When fully developed between 300000 and 320000
  if (energy < (desiredBuffer + 20000)) {
    return ECONOMY_STABLE
  }

  // Need to ditch energy as we have way too much in storage
  if (_.sum(this.storage.storage) > this.storage.storeCapacity * 0.9) {
    return ECONOMY_BURSTING
  }

  // When fully developed over 320000
  return ECONOMY_SURPLUS
}

Room.prototype.getEnergyAmount = function () {
  let energy = 0
  if (this.storage && this.storage.store[RESOURCE_ENERGY]) {
    energy += this.storage.store[RESOURCE_ENERGY]
  }
  if (this.terminal && this.terminal.store[RESOURCE_ENERGY]) {
    energy += this.terminal.store[RESOURCE_ENERGY]
  }
  return energy
}

Room.prototype.getDesiredEnergyBuffer = function () {
  const roomLevel = this.getPracticalRoomLevel()
  if (roomLevel < 4) {
    return 0
  }
  return Math.min((roomLevel - 3) * 100000, 300000)
}
<commit_msg>Lower desired energy buffer for simulator<commit_after>'use strict'

global.ECONOMY_CRASHED = 0
global.ECONOMY_FALTERING = 1
global.ECONOMY_DEVELOPING = 2
global.ECONOMY_STABLE = 3
global.ECONOMY_SURPLUS = 4
global.ECONOMY_BURSTING = 5

const economySettings = {
  'EXPAND_FROM': ECONOMY_DEVELOPING,
  'WALLBUILDERS': ECONOMY_DEVELOPING,
  'BUILD_STRUCTURES': ECONOMY_DEVELOPING,

  'EXTRACT_MINERALS': ECONOMY_STABLE,
  'UPGRADE_CONTROLLERS': ECONOMY_STABLE,

  'EXTRA_UPGRADERS': ECONOMY_SURPLUS,
  'EXTRA_WALLBUILDERS': ECONOMY_SURPLUS,

  'MORE_EXTRA_UPGRADERS': ECONOMY_BURSTING
}

Room.prototype.isEconomyCapable = function (key) {
  if (!economySettings[key]) {
    return false
  }
  return this.getEconomyLevel() >= economySettings[key]
}

Room.prototype.getEconomyLevel = function () {
  if (this.getPracticalRoomLevel() < 4) {
    return ECONOMY_STABLE
  }

  const desiredBuffer = this.getDesiredEnergyBuffer()
  const energy = this.getEnergyAmount()

  if (energy < 15000) {
    return ECONOMY_CRASHED
  }

  // When fully developed between 15000 and 280000
  if (energy < (desiredBuffer - 20000)) {
    return ECONOMY_FALTERING
  }

  // When fully developed between 280000 and 300000
  if (energy < (desiredBuffer)) {
    return ECONOMY_DEVELOPING
  }

  // When fully developed between 300000 and 320000
  if (energy < (desiredBuffer + 20000)) {
    return ECONOMY_STABLE
  }

  // Need to ditch energy as we have way too much in storage
  if (_.sum(this.storage.storage) > this.storage.storeCapacity * 0.9) {
    return ECONOMY_BURSTING
  }

  // When fully developed over 320000
  return ECONOMY_SURPLUS
}

Room.prototype.getEnergyAmount = function () {
  let energy = 0
  if (this.storage && this.storage.store[RESOURCE_ENERGY]) {
    energy += this.storage.store[RESOURCE_ENERGY]
  }
  if (this.terminal && this.terminal.store[RESOURCE_ENERGY]) {
    energy += this.terminal.store[RESOURCE_ENERGY]
  }
  return energy
}

Room.prototype.getDesiredEnergyBuffer = function () {
  const roomLevel = this.getPracticalRoomLevel()
  if (roomLevel < 4) {
    return 0
  }
  if (this.name === 'sim') {
    return 40000
  }
  return Math.min((roomLevel - 3) * 100000, 300000)
}
<|endoftext|>