<commit_before>var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;
var utils = require('../utils.js');
var net = require('net');
var Future = require('fibers/future');
var _ = require('underscore');
var files = require('../files.js');
var httpHelpers = require('../http-helpers.js');

var MONGO_LISTENING =
  { stdout: " [initandlisten] waiting for connections on port" };

var SIMPLE_WAREHOUSE = {
  v1: { },
  v2: { recommended: true },
  v3: { }
};

selftest.define("run", function () {
  var s = new Sandbox({ fakeMongo: true });
  var run;

  // Starting a run
  s.createApp("myapp", "standard-app");
  s.cd("myapp");
  s.set("METEOR_TEST_TMP", files.mkdtemp());
  run = s.run();
  run.match("myapp");
  run.match("proxy");
  run.tellMongo(MONGO_LISTENING);
  run.match("MongoDB");
  run.match("your app");
  run.waitSecs(5);
  run.match("running at");
  run.match("localhost");

  // File change
  s.write("empty.js", "");
  run.waitSecs(2);
  run.match("restarted");
  s.write("empty.js", " ");
  run.waitSecs(2);
  run.match("restarted (x2)");
  // XXX want app to generate output so that we can see restart counter reset

  // Crashes
  s.write("crash.js", "process.exit(42);");
  run.waitSecs(5);
  run.match("with code: 42");
  run.waitSecs(5);
  run.match("is crashing");
  s.unlink("crash.js");
  run.waitSecs(5);
  run.match("Modified");
  run.waitSecs(5);
  run.match("restarted");
  s.write("empty.js", "");
  run.waitSecs(5);
  run.match("restarted (x2)"); // see that restart counter reset
  s.write("crash.js", "process.kill(process.pid, 'SIGKILL');");
  run.waitSecs(5);
  run.match("from signal: SIGKILL");
  run.waitSecs(5);
  run.match("is crashing");

  // Bundle failure
  s.unlink("crash.js");
  s.write("junk.js", "]");
  run.waitSecs(5);
  run.match("Modified");
  run.match("prevented startup");
  run.match("Unexpected token");
  run.match("file change");

  // Back to working
  s.unlink("junk.js");
  run.waitSecs(5);
  run.match("restarted");

  // Crash just once, then restart successfully
  s.write("crash.js",
"var fs = Npm.require('fs')\n" +
"var path = Npm.require('path')\n" +
"var crashmark = path.join(process.env.METEOR_TEST_TMP, 'crashed');\n" +
"try {\n" +
"  fs.readFileSync(crashmark);\n" +
"} catch (e) {\n" +
"  fs.writeFileSync(crashmark);\n" +
"  process.exit(137);\n" +
"}\n");
  run.waitSecs(5);
  run.match("with code: 137");
  run.waitSecs(5);
  run.match("restarted");
  run.stop();

  // How about a bundle failure right at startup
  s.unlink("crash.js");
  s.write("junk.js", "]");
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("prevented startup");
  run.match("Unexpected token");
  run.match("file change");
  s.unlink("junk.js");
  run.waitSecs(5);
  run.match("restarted");
  run.stop();

// XXX --port, --production, --raw-logs, --settings, --program
});

selftest.define("run --once", function () {
  var s = new Sandbox({ fakeMongo: true });
  var run;

  s.createApp("onceapp", "once");
  s.cd("onceapp");

  // Basic run --once
  s.set("RUN_ONCE_OUTCOME", "exit");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("once test\n");
  run.expectExit(123);

  // run --once, exit on signal
  s.set("RUN_ONCE_OUTCOME", "kill");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("once test\n");
  run.matchErr("Killed (SIGKILL)\n");
  run.expectExit(255);

  // run --once, bundle failure
  s.set("RUN_ONCE_OUTCOME", "exit");
  s.write("junk.js", "]");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.matchErr("Build failed");
  run.matchErr("Unexpected token");
  run.expectExit(254);
  s.unlink("junk.js");

  // file changes don't make it restart
  s.set("RUN_ONCE_OUTCOME", "hang");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("once test\n");
  s.write('empty.js', 'null');
  var originalRelease = s.read('.meteor/release');
  s.write('.meteor/release', 'v1');
  utils.sleepMs(2000); // sorry, hard to avoid
  run.stop();
  run.forbidAll("updated");
  s.unlink('empty.js');
  s.write('.meteor/release', originalRelease);

  // running a different program
  run = s.run("--once", "--program", "other");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(8);
  run.match("other program\n");
  run.expectExit(44);

  // bad program name
  run = s.run("--once", "--program", "xyzzy");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("'xyzzy' not found");
  run.expectExit(254);

  // Try it with a real Mongo. Make sure that it actually starts one.
  s = new Sandbox;
  s.createApp("onceapp", "once");
  s.cd("onceapp");
  s.set("RUN_ONCE_OUTCOME", "mongo");
  run = s.run("--once");
  run.waitSecs(30);
  run.expectExit(86);
});

selftest.define("run errors", function () {
  var s = new Sandbox;
  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  // Prevent mongod from starting up.  (Note that "127.0.0.1" matches the
  // interface that mongo uses.)
  var proxyPort = utils.randomPort();
  var mongoPort = proxyPort + 1;
  var f = new Future;
  var server = net.createServer().listen(mongoPort, "127.0.0.1", f.resolver());
  f.wait();

  var run = s.run("-p", proxyPort);
  _.times(3, function () {
    run.waitSecs(30);
    run.match("Unexpected mongo exit code 48. Restarting.");
  });
  run.waitSecs(3);
  run.match("Can't start Mongo server");
  run.match("MongoDB exited because its port was closed");
  run.match("running in the same project.\n");
  run.expectEnd();
  run.forbid("Started MongoDB");
  run.expectExit(254);

  f = new Future;
  server.close(f.resolver());
  f.wait();

  // This time, prevent the proxy from starting. (This time, leaving out the
  // interface name matches.)
  f = new Future;
  server = net.createServer().listen(proxyPort, f.resolver());
  f.wait();

  run = s.run("-p", proxyPort);
  run.waitSecs(3);
  run.match(/Can't listen on port.*another Meteor/);
  run.expectExit(254);

  f = new Future;
  server.close(f.resolver());
  f.wait();
});

selftest.define("update during run", ["checkout"], function () {
  var s = new Sandbox({
    warehouse: SIMPLE_WAREHOUSE,
    fakeMongo: true
  });
  var run;

  s.createApp("myapp", "packageless");
  s.cd("myapp");

  // This makes packages not depend on meteor (specifically, makes our empty
  // control program not depend on meteor).
  s.set("NO_METEOR_PACKAGE", "t");

  // If the app version changes, we exit with an error message.
  s.write('.meteor/release', 'METEOR@v1');
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  run.matchErr('to Meteor METEOR@v2 from Meteor METEOR@v1');
  run.expectExit(254);

  // But not if the release was forced (case 1)
  s.write('.meteor/release', 'METEOR@v1');
  run = s.run("--release", "METEOR@v3");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  s.write('empty.js', '');
  run.waitSecs(2);
  run.match('restarted');
  run.stop();
  run.forbidAll("updated");

  // But not if the release was forced (case 2)
  s.write('.meteor/release', 'METEOR@v1');
  run = s.run("--release", "METEOR@v1");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  s.write('empty.js', '');
  run.waitSecs(2);
  run.match('restarted');
  run.stop();
  run.forbidAll("updated");

  // Nor do we do it if you're running from a checkout
  s = new Sandbox({ fakeMongo: true });
  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  s.write('.meteor/release', 'METEOR@v1');
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  s.write('empty.js', '');
  run.waitSecs(2);
  run.match('restarted');
  run.stop();
  run.forbidAll("updated");
});

selftest.define("run with mongo crash", ["checkout"], function () {
  var s = new Sandbox({ fakeMongo: true });
  var run;

  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  // Kill mongod three times.  See that it gives up and quits.
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000/\n');
  run.tellMongo({exit: 23});
  run.read('Unexpected mongo exit code 23. Restarting.\n');
  run.tellMongo({exit: 46});
  run.read('Unexpected mongo exit code 46. Restarting.\n');
  run.tellMongo({exit: 47});
  run.read('Unexpected mongo exit code 47. Restarting.\n');
  run.read("Can't start Mongo server.\n");
  run.read("MongoDB exited due to excess clock skew\n");
  run.expectEnd();
  run.expectExit(254);

  // Now create a build failure. Make sure that killing mongod three times
  // *also* successfully quits even if we're waiting on file change.
  s.write('bad.js', ']');
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match("prevented startup");
  run.match("file change.\n");
  run.tellMongo({exit: 23});
  run.read('Unexpected mongo exit code 23. Restarting.\n');
  run.tellMongo({exit: 46});
  run.read('Unexpected mongo exit code 46. Restarting.\n');
  run.tellMongo({exit: 47});
  run.read('Unexpected mongo exit code 47. Restarting.\n');
  run.read("Can't start Mongo server.\n");
  run.read("MongoDB exited due to excess clock skew\n");
  run.expectEnd();
  run.expectExit(254);
});

// Test that when the parent runner process is SIGKILLed, the child
// process exits also.
selftest.define("run and SIGKILL parent process", function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "app-prints-pid");
  s.cd("myapp");

  run = s.run();
  run.waitSecs(30);
  var match = run.match(/My pid is (\d+)/);
  var childPid;
  if (! match || ! match[1]) {
    selftest.fail("No pid printed");
  }
  childPid = match[1];

  process.kill(run.proc.pid, "SIGKILL");
  // This sleep should be a little more time than the interval at which
  // the child checks if the parent is still alive, in
  // packages/webapp/webapp_server.js.
  utils.sleepMs(3500);

  // Send the child process a signal of 0. If there is no error, it
  // means that the process is still running, which is not what we
  // expect.
  var caughtError;
  try {
    process.kill(childPid, 0);
  } catch (err) {
    caughtError = err;
  }

  if (! caughtError) {
    selftest.fail("Child process " + childPid + " is still running");
  }

  run.stop();

  // Test that passing a bad pid in --parent-pid logs an error and exits
  // immediately.
  s.set("METEOR_BAD_PARENT_PID_FOR_TEST", "t");
  run = s.run();
  run.waitSecs(30);
  run.match("must be a valid process ID");
  run.match("Your application is crashing");
  run.stop();
});

selftest.define("'meteor run --port' requires a port", function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "app-prints-pid");
  s.cd("myapp");

  run = s.run("run", "--port", "example.com");
  run.waitSecs(30);
  run.matchErr("--port must include a port");
  run.expectExit(1);

  run = s.run("run", "--port", "http://example.com");
  run.waitSecs(30);
  run.matchErr("--port must include a port");
  run.expectExit(1);
});
<commit_msg>make 'run' test pass<commit_after>var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;
var utils = require('../utils.js');
var net = require('net');
var Future = require('fibers/future');
var _ = require('underscore');
var files = require('../files.js');
var httpHelpers = require('../http-helpers.js');

var MONGO_LISTENING =
  { stdout: " [initandlisten] waiting for connections on port" };

var SIMPLE_WAREHOUSE = {
  v1: { },
  v2: { recommended: true },
  v3: { }
};

selftest.define("run", function () {
  var s = new Sandbox({ fakeMongo: true });
  var run;

  // Starting a run
  s.createApp("myapp", "standard-app");
  s.cd("myapp");
  s.set("METEOR_TEST_TMP", files.mkdtemp());
  run = s.run();
  run.match("myapp");
  run.match("proxy");
  run.tellMongo(MONGO_LISTENING);
  run.match("MongoDB");
  run.match("your app");
  run.waitSecs(5);
  run.match("running at");
  run.match("localhost");

  // File change
  s.write("empty.js", "");
  run.waitSecs(2);
  run.match("restarted");
  s.write("empty.js", " ");
  run.waitSecs(2);
  run.match("restarted");
  // XXX want app to generate output so that we can see restart counter reset

  // Crashes
  s.write("crash.js", "process.exit(42);");
  run.waitSecs(5);
  run.match("with code: 42");
  run.waitSecs(5);
  run.match("is crashing");
  s.unlink("crash.js");
  run.waitSecs(5);
  run.match("Modified");
  run.waitSecs(5);
  run.match("restarted");
  s.write("empty.js", "");
  run.waitSecs(5);
  // We used to see the restart counter reset but right now restart messages
  // don't coalesce due to intermediate use of the progress bar.
  run.match("restarted");
  s.write("crash.js", "process.kill(process.pid, 'SIGKILL');");
  run.waitSecs(5);
  run.match("from signal: SIGKILL");
  run.waitSecs(5);
  run.match("is crashing");

  // Bundle failure
  s.unlink("crash.js");
  s.write("junk.js", "]");
  run.waitSecs(5);
  run.match("Modified");
  run.match("prevented startup");
  run.match("Unexpected token");
  run.match("file change");

  // Back to working
  s.unlink("junk.js");
  run.waitSecs(5);
  run.match("restarted");

  // Crash just once, then restart successfully
  s.write("crash.js",
"var fs = Npm.require('fs')\n" +
"var path = Npm.require('path')\n" +
"var crashmark = path.join(process.env.METEOR_TEST_TMP, 'crashed');\n" +
"try {\n" +
"  fs.readFileSync(crashmark);\n" +
"} catch (e) {\n" +
"  fs.writeFileSync(crashmark);\n" +
"  process.exit(137);\n" +
"}\n");
  run.waitSecs(5);
  run.match("with code: 137");
  run.waitSecs(5);
  run.match("restarted");
  run.stop();

  // How about a bundle failure right at startup
  s.unlink("crash.js");
  s.write("junk.js", "]");
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("prevented startup");
  run.match("Unexpected token");
  run.match("file change");
  s.unlink("junk.js");
  run.waitSecs(5);
  run.match("restarted");
  run.stop();

// XXX --port, --production, --raw-logs, --settings, --program
});

selftest.define("run --once", function () {
  var s = new Sandbox({ fakeMongo: true });
  var run;

  s.createApp("onceapp", "once");
  s.cd("onceapp");

  // Basic run --once
  s.set("RUN_ONCE_OUTCOME", "exit");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("once test\n");
  run.expectExit(123);

  // run --once, exit on signal
  s.set("RUN_ONCE_OUTCOME", "kill");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("once test\n");
  run.matchErr("Killed (SIGKILL)\n");
  run.expectExit(255);

  // run --once, bundle failure
  s.set("RUN_ONCE_OUTCOME", "exit");
  s.write("junk.js", "]");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.matchErr("Build failed");
  run.matchErr("Unexpected token");
  run.expectExit(254);
  s.unlink("junk.js");

  // file changes don't make it restart
  s.set("RUN_ONCE_OUTCOME", "hang");
  run = s.run("--once");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("once test\n");
  s.write('empty.js', 'null');
  var originalRelease = s.read('.meteor/release');
  s.write('.meteor/release', 'v1');
  utils.sleepMs(2000); // sorry, hard to avoid
  run.stop();
  run.forbidAll("updated");
  s.unlink('empty.js');
  s.write('.meteor/release', originalRelease);

  // running a different program
  run = s.run("--once", "--program", "other");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(8);
  run.match("other program\n");
  run.expectExit(44);

  // bad program name
  run = s.run("--once", "--program", "xyzzy");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(5);
  run.match("'xyzzy' not found");
  run.expectExit(254);

  // Try it with a real Mongo. Make sure that it actually starts one.
  s = new Sandbox;
  s.createApp("onceapp", "once");
  s.cd("onceapp");
  s.set("RUN_ONCE_OUTCOME", "mongo");
  run = s.run("--once");
  run.waitSecs(30);
  run.expectExit(86);
});

selftest.define("run errors", function () {
  var s = new Sandbox;
  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  // Prevent mongod from starting up.  (Note that "127.0.0.1" matches the
  // interface that mongo uses.)
  var proxyPort = utils.randomPort();
  var mongoPort = proxyPort + 1;
  var f = new Future;
  var server = net.createServer().listen(mongoPort, "127.0.0.1", f.resolver());
  f.wait();

  var run = s.run("-p", proxyPort);
  _.times(3, function () {
    run.waitSecs(30);
    run.match("Unexpected mongo exit code 48. Restarting.");
  });
  run.waitSecs(3);
  run.match("Can't start Mongo server");
  run.match("MongoDB exited because its port was closed");
  run.match("running in the same project.\n");
  run.expectEnd();
  run.forbid("Started MongoDB");
  run.expectExit(254);

  f = new Future;
  server.close(f.resolver());
  f.wait();

  // This time, prevent the proxy from starting. (This time, leaving out the
  // interface name matches.)
  f = new Future;
  server = net.createServer().listen(proxyPort, f.resolver());
  f.wait();

  run = s.run("-p", proxyPort);
  run.waitSecs(3);
  run.match(/Can't listen on port.*another Meteor/);
  run.expectExit(254);

  f = new Future;
  server.close(f.resolver());
  f.wait();
});

selftest.define("update during run", ["checkout"], function () {
  var s = new Sandbox({
    warehouse: SIMPLE_WAREHOUSE,
    fakeMongo: true
  });
  var run;

  s.createApp("myapp", "packageless");
  s.cd("myapp");

  // This makes packages not depend on meteor (specifically, makes our empty
  // control program not depend on meteor).
  s.set("NO_METEOR_PACKAGE", "t");

  // If the app version changes, we exit with an error message.
  s.write('.meteor/release', 'METEOR@v1');
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  run.matchErr('to Meteor METEOR@v2 from Meteor METEOR@v1');
  run.expectExit(254);

  // But not if the release was forced (case 1)
  s.write('.meteor/release', 'METEOR@v1');
  run = s.run("--release", "METEOR@v3");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  s.write('empty.js', '');
  run.waitSecs(2);
  run.match('restarted');
  run.stop();
  run.forbidAll("updated");

  // But not if the release was forced (case 2)
  s.write('.meteor/release', 'METEOR@v1');
  run = s.run("--release", "METEOR@v1");
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  s.write('empty.js', '');
  run.waitSecs(2);
  run.match('restarted');
  run.stop();
  run.forbidAll("updated");

  // Nor do we do it if you're running from a checkout
  s = new Sandbox({ fakeMongo: true });
  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  s.write('.meteor/release', 'METEOR@v1');
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000');
  s.write('.meteor/release', 'METEOR@v2');
  s.write('empty.js', '');
  run.waitSecs(2);
  run.match('restarted');
  run.stop();
  run.forbidAll("updated");
});

selftest.define("run with mongo crash", ["checkout"], function () {
  var s = new Sandbox({ fakeMongo: true });
  var run;

  s.createApp("myapp", "standard-app");
  s.cd("myapp");

  // Kill mongod three times.  See that it gives up and quits.
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match('localhost:3000/\n');
  run.tellMongo({exit: 23});
  run.read('Unexpected mongo exit code 23. Restarting.\n');
  run.tellMongo({exit: 46});
  run.read('Unexpected mongo exit code 46. Restarting.\n');
  run.tellMongo({exit: 47});
  run.read('Unexpected mongo exit code 47. Restarting.\n');
  run.read("Can't start Mongo server.\n");
  run.read("MongoDB exited due to excess clock skew\n");
  run.expectEnd();
  run.expectExit(254);

  // Now create a build failure. Make sure that killing mongod three times
  // *also* successfully quits even if we're waiting on file change.
  s.write('bad.js', ']');
  run = s.run();
  run.tellMongo(MONGO_LISTENING);
  run.waitSecs(2);
  run.match("prevented startup");
  run.match("file change.\n");
  run.tellMongo({exit: 23});
  run.read('Unexpected mongo exit code 23. Restarting.\n');
  run.tellMongo({exit: 46});
  run.read('Unexpected mongo exit code 46. Restarting.\n');
  run.tellMongo({exit: 47});
  run.read('Unexpected mongo exit code 47. Restarting.\n');
  run.read("Can't start Mongo server.\n");
  run.read("MongoDB exited due to excess clock skew\n");
  run.expectEnd();
  run.expectExit(254);
});

// Test that when the parent runner process is SIGKILLed, the child
// process exits also.
selftest.define("run and SIGKILL parent process", function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "app-prints-pid");
  s.cd("myapp");

  run = s.run();
  run.waitSecs(30);
  var match = run.match(/My pid is (\d+)/);
  var childPid;
  if (! match || ! match[1]) {
    selftest.fail("No pid printed");
  }
  childPid = match[1];

  process.kill(run.proc.pid, "SIGKILL");
  // This sleep should be a little more time than the interval at which
  // the child checks if the parent is still alive, in
  // packages/webapp/webapp_server.js.
  utils.sleepMs(3500);

  // Send the child process a signal of 0. If there is no error, it
  // means that the process is still running, which is not what we
  // expect.
  var caughtError;
  try {
    process.kill(childPid, 0);
  } catch (err) {
    caughtError = err;
  }

  if (! caughtError) {
    selftest.fail("Child process " + childPid + " is still running");
  }

  run.stop();

  // Test that passing a bad pid in --parent-pid logs an error and exits
  // immediately.
  s.set("METEOR_BAD_PARENT_PID_FOR_TEST", "t");
  run = s.run();
  run.waitSecs(30);
  run.match("must be a valid process ID");
  run.match("Your application is crashing");
  run.stop();
});

selftest.define("'meteor run --port' requires a port", function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "app-prints-pid");
  s.cd("myapp");

  run = s.run("run", "--port", "example.com");
  run.waitSecs(30);
  run.matchErr("--port must include a port");
  run.expectExit(1);

  run = s.run("run", "--port", "http://example.com");
  run.waitSecs(30);
  run.matchErr("--port must include a port");
  run.expectExit(1);
});
<|endoftext|>


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

/* eslint-disable no-console */

const {cp, set} = require("shelljs");
const path = require("path");

const filesToVendor = {
  // XXX currently these two licenses are identical.  Perhaps we should check
  // in case that changes at some point in the future.
  "react/LICENSE": "REACT_AND_REACT_DOM_LICENSE",
  "react/umd/react.production.min.js": "react.js",
  "react/umd/react.development.js": "react-dev.js",
  "react-dom/umd/react-dom.production.min.js": "react-dom.js",
  "react-dom/umd/react-dom.development.js": "react-dom-dev.js",
  "react-intl/LICENSE.md": "REACT_INTL_LICENSE",
  "react-intl/dist/react-intl.min.js": "react-intl.js",
};

set("-v"); // Echo all the copy commands so the user can see what's going on
for (let srcPath of Object.keys(filesToVendor)) {
  cp(path.join("node_modules", srcPath),
    path.join("vendor", filesToVendor[srcPath]));
}

console.log(`
Check to see if any license files have changed, and, if so, be sure to update
https://searchfox.org/mozilla-central/source/toolkit/content/license.html`);
<commit_msg>Followup bug 1459450 - Actually remove vendor-react.js<commit_after><|endoftext|>


<commit_before>// jshint ignore: start

/*

a = canvases
b = body
s = canvas size
t = text size

I = intervals
T = tick

X, Y, Z, and _ are temporary

*/

I = 'sec min hr day wk mon yr'.split(' ');

document.documentElement.style.cssText = 'width:100%;height:100%;display:table;background-color:#333';
b.style.cssText = 'display:table-cell;vertical-align:middle;text-align:center';

t = Infinity;
a = I.map(function(unit, index) {

  _ = document.createElement('canvas');
  s = _.width = _.height = innerWidth / 7;
  b.style.fontSize = (s / 8) + 'px';

  _.u = unit;

  _.m = [1000, 60000, 3600000, 86400000, 604800000, 2629800000, 31557600000][index];

  _.e = function(result) {
    result = new Date;
    switch (unit) {
      case 'yr':
        result.setMonth(0);
      case 'mon':
        result.setDate(1);
      case 'wk':
      case 'day':
        result.setHours(0);
      case 'hr':
        result.setMinutes(0);
      case 'min':
        result.setSeconds(0);
      case 'sec':
        result.setMilliseconds(0);
    }
    if (unit === 'wk') {
      result.setDate(result.getDate() - result.getDay());
    }
    result.setTime(result.getTime() + this.m - 1);
    return result;
  };

  _.c = _.getContext('2d');
  _.c.fillStyle = _.c.strokeStyle = ['#fa8072', '#faad72', '#fada72', '#acda72', '#87ceeb', '#87aceb', '#a4b7eb'][index];
  _.c.lineWidth = s * 0.01;
  _.c.textAlign = 'center';
  _.c.textBaseline = 'middle';

  b.appendChild(_);

  $ = 1;
  do {
    _.c.font = '100 ' + $ + 'px sans-serif';
    $ ++;
  } while (_.c.measureText(unit).width < (s * 0.8));
  t = Math.min(t, $);

  return _;

});

(function T() {

  a.forEach(function(canvas) {

    canvas.c.clearRect(0, 0, s, s);

    canvas.c.beginPath();
    canvas.c.moveTo(s / 2, s / 2);
    canvas.c.arc(s / 2, s / 2, s * 0.45, 0, 2 * Math.PI * ((canvas.e() - new Date) / canvas.m));
    canvas.c.fill();

    canvas.c.beginPath();
    canvas.c.moveTo(s / 2, s / 2);
    canvas.c.arc(s / 2, s / 2, s * 0.45, 0, 2 * Math.PI);
    canvas.c.stroke();

    canvas.c.font = '100 ' + t + 'px sans-serif';
    canvas.c.fillText(canvas.u, s / 2, s / 2);

  });

  requestAnimationFrame(T);

})();
<commit_msg>-1 byte<commit_after>// jshint ignore: start

/*

a = canvases
b = body
s = canvas size
t = text size

I = intervals
T = tick

X, Y, Z, and _ are temporary

*/

I = 'sec min hr day wk mon yr'.split(' ');

document.documentElement.style.cssText = 'width:100%;height:100%;display:table;background-color:#333';
b.style.cssText = 'display:table-cell;vertical-align:middle;text-align:center';

t = Infinity;
a = I.map(function(unit, index) {

  _ = document.createElement('canvas');
  s = _.width = _.height = innerWidth / 7;
  b.style.fontSize = (s / 8) + 'px';

  _.u = unit;

  _.m = [1000, 60000, 3600000, 86400000, 604800000, 2629800000, 31557600000][index];

  _.e = function() {
    Z = new Date;
    switch (unit) {
      case 'yr':
        Z.setMonth(0);
      case 'mon':
        Z.setDate(1);
      case 'wk':
      case 'day':
        Z.setHours(0);
      case 'hr':
        Z.setMinutes(0);
      case 'min':
        Z.setSeconds(0);
      case 'sec':
        Z.setMilliseconds(0);
    }
    if (unit === 'wk') {
      Z.setDate(Z.getDate() - Z.getDay());
    }
    Z.setTime(Z.getTime() + this.m - 1);
    return Z;
  };

  _.c = _.getContext('2d');
  _.c.fillStyle = _.c.strokeStyle = ['#fa8072', '#faad72', '#fada72', '#acda72', '#87ceeb', '#87aceb', '#a4b7eb'][index];
  _.c.lineWidth = s * 0.01;
  _.c.textAlign = 'center';
  _.c.textBaseline = 'middle';

  b.appendChild(_);

  $ = 1;
  do {
    _.c.font = '100 ' + $ + 'px sans-serif';
    $ ++;
  } while (_.c.measureText(unit).width < (s * 0.8));
  t = Math.min(t, $);

  return _;

});

(function T() {

  a.forEach(function(canvas) {

    canvas.c.clearRect(0, 0, s, s);

    canvas.c.beginPath();
    canvas.c.moveTo(s / 2, s / 2);
    canvas.c.arc(s / 2, s / 2, s * 0.45, 0, 2 * Math.PI * ((canvas.e() - new Date) / canvas.m));
    canvas.c.fill();

    canvas.c.beginPath();
    canvas.c.moveTo(s / 2, s / 2);
    canvas.c.arc(s / 2, s / 2, s * 0.45, 0, 2 * Math.PI);
    canvas.c.stroke();

    canvas.c.font = '100 ' + t + 'px sans-serif';
    canvas.c.fillText(canvas.u, s / 2, s / 2);

  });

  requestAnimationFrame(T);

})();
<|endoftext|>


<commit_before>'use strict';

module.exports = {
    $meta: 'English translation file',
    navigation: {
        overview: {
            $filter: 'role',
            district: 'ज़िला प्रदर्शन',
            block: 'प्रखंड/जनपद प्रदर्शन',
            $default: 'अवलोकन प्रदर्शन'
        },
        discrete: {
            $filter: 'role',
            district: 'प्रखंड/जनपद प्रदर्शन',
            block: 'पंचायत प्रदर्शन',
            $default: 'अलहदा प्रदर्शन'
        },
        current: 'वर्तमान मस्टर्स',
        delayed: 'विलंबित मस्टर्स'
    },
    messages: {
        loading: 'डेटा लोड हो रहा है...',
        not_found: 'पृष्ठ नहीं मिला। Paydash टीम से संपर्क करें.'
    },
    time_selector :{
        '1': 'सारी उपलब्ध तिथियाँ',
        '2': 'पिछले 30 दिन',
        '3': 'पिछले 60 दिन'
    },
    payment_steps: {
        '1': 'मस्टर रोल बंद से डाटा एंट्री का समय',
        '2': 'डाटा एंट्री से वेज लिस्ट बनाने का समय',
        '3': 'वेज लिस्ट बनाने से वेज लिस्ट भेजने का समय',
        '4': 'वेज लिस्ट भेजने से FTO बनाने का समय',
        '5': 'FTO बनाने से पहले हस्ताक्षर का समय',
        '6': 'पहले हस्ताक्षर से दूसरे हस्ताक्षर का समय',
        '7': 'दूसरे हस्ताक्षर से बैंक की कारवाई के समापन का समय'
    },
    payment_steps_labels: [
        'मस्टर रोल बंद से डाटा एंट्री का समय',
        'डाटा एंट्री से वेज लिस्ट बनाने का समय',
        'वेज लिस्ट बनाने से वेज लिस्ट भेजने का समय',
        'वेज लिस्ट भेजने से FTO बनाने का समय',
        'FTO बनाने से पहले हस्ताक्षर का समय',
        'पहले हस्ताक्षर से दूसरे हस्ताक्षर का समय',
        'दूसरे हस्ताक्षर से बैंक की कारवाई के समापन का समय'
    ],
    compare_chart_labels:{
        'state': 'राज्य औसत',
        'district': 'ज़िला औसत',
        'block': 'प्रखंड/जनपद औसत'
    },
    y_axis_labels: 'प्रक्रिया पूरी करने में लगे दिन',
    total_trans :"कुल लेनदेन",
    performance: {
        overview: {
            chart_a: {
                title: {
                    $filter: 'role',
                    district: 'आपके ज़िले का प्रदर्शन',
                    block: 'आपके प्रखंड/जनपद का प्रदर्शन',
                    $default: 'अवलोकन प्रदर्शन'
                },
                description: {
                    $filter: 'role',
                    district: 'आपके ज़िले में मज़दूरी भुगतान के हर एक पड़ाव पर लगे औसत दिन',
                    block: 'आपके प्रखंड/जनपद में मज़दूरी भुगतान के हर एक पड़ाव पर लगे औसत दिन',
                    $default: 'आपके क्षेत्रों में मज़दूरी भुगतान के हर एक पड़ाव पर लगे औसत दिन'
                }
            },
            chart_b: {
                title: {
                    $filter: 'role',
                    district: 'आपके प्रदर्शन की तुलना',
                    block: 'आपके प्रदर्शन की तुलना',
                    $default: 'आपके प्रदर्शन की तुलना'
                },
                description: {
                    $filter: 'role',
                    district: 'अपने ज़िले के औसत प्रदर्शन की तुलना राज्य के औसत प्रदर्शन से करें',
                    block: 'अपने प्रखंड/जनपद के औसत प्रदर्शन की तुलना ज़िले और राज्य के औसत प्रदर्शन से करें ',
                    $default: 'अपने प्रखंड/जनपद के औसत प्रदर्शन की तुलना अन्य क्षेत्रों के औसत प्रदर्शन से करें'
                }
            },
            tooltip: 'यह ग्राफ़ MGNREGA मज़दूरी भुगतान में लगा औसत समय दिखाता है| वर्णित तिथियों पर हुए भुगतान में लगे समय को 7 पड़ावों में बाटा गया है| इसलिए, केवल पूरे हुए भुगतान का डाटा दिखाया जा रहा है|',
        },
        discrete:{
            sub_heading: {
                '1' : 'आपकी पंचायत',
                '2' : 'का प्रदर्शन'
            },
            subtitle: {
                $filter: 'role',
                block: 'आपकी पंचायत में मज़दूरी भुगतान की प्रक्रिया के हर एक पड़ाव में लगा औसत समय',
                district: 'The performance of your blocks on average days to complete each step of the payment process.',
                $default :'अपने क्षेत्रों में मज़दूरी भुगतान की प्रक्रिया के हर एक पड़ाव में लगा औसत समय'
            },
            tooltip: 'यह ग्राफ़ MGNREGA मज़दूरी भुगतान में लगा औसत समय दिखाता है| वर्णित तिथियों पर हुए भुगतान में लगे समय को 7 पड़ावों में बाटा गया है| इसलिए, केवल पूरे हुए भुगतान का डाटा दिखाया जा रहा है|',
            ta_message: 'आपके प्रखंड/जनपद में कुछ TAs/SEs/JEs और उनकी पंचायतों के नाम MGNREGA वेबसाइट पर अपडेट नहीं किए गये हैं| इस के परिणाम स्वरूप, हम आपको आपके प्रखंड/जनपद के सभी TAs/SEs/JEs  के प्रदर्शन की जानकारी नहीं दे सकते| यह जानकारी पाने के लिए कृपया nrega.nic.in पर सभी TAs/SEs/JEs  और उनकी पंचायतों के नाम भरें|',
            grs_message: 'आपके प्रखंड/जनपद में कुछ GRSs और उनकी पंचायतों के नाम MGNREGA वेबसाइट पर अपडेट नहीं किए गये हैं| इस के परिणाम स्वरूप, हम आपको आपके प्रखंड/जनपद के सभी GRSs के प्रदर्शन की जानकारी नहीं दे सकते| यह जानकारी पाने के लिए कृपया nrega.nic.in पर सभी GRSs और उनकी पंचायतों के नाम भरें|',
            grouping_selectors: {
                no: 'बिना कोई वर्गीकरण',
                ta:'वर्ग: उप यंत्री',
                grs: 'वर्ग: GRS'
            },
            panchayat_chart_placeholder: 'भुगतान के प्रदर्शन को देखने के लिए एक पंचायत का चयन करें',
        }
    },
    musters: {
        current: {
            title: 'आज बंद हो रहे मस्टर्स'
        },
        delayed:{
            title : 'विलंबित मस्टर्स',
            t_2 : 'अटेंडेन्स नहीं भरी गयी (T+2)',
            t_5: 'MB नहीं भरी गयी (T+5)',
            t_6 : 'वेज लिस्ट नहीं भेजी गयी (T+6)',
            t_7 :'FTO पर पहला हस्ताक्षर नहीं हुआ (T+7)',
            T_8 :'FTO पर दूसरा हस्ताक्षर नहीं हुआ (T+8)'
        }
    }
};
<commit_msg>Fix typo in translation<commit_after>'use strict';

module.exports = {
    $meta: 'English translation file',
    navigation: {
        overview: {
            $filter: 'role',
            district: 'ज़िला प्रदर्शन',
            block: 'प्रखंड/जनपद प्रदर्शन',
            $default: 'अवलोकन प्रदर्शन'
        },
        discrete: {
            $filter: 'role',
            district: 'प्रखंड/जनपद प्रदर्शन',
            block: 'पंचायत प्रदर्शन',
            $default: 'अलहदा प्रदर्शन'
        },
        current: 'वर्तमान मस्टर्स',
        delayed: 'विलंबित मस्टर्स'
    },
    messages: {
        loading: 'डेटा लोड हो रहा है...',
        not_found: 'पृष्ठ नहीं मिला। Paydash टीम से संपर्क करें.'
    },
    time_selector :{
        '1': 'सारी उपलब्ध तिथियाँ',
        '2': 'पिछले 60 दिन',
        '3': 'पिछले 30 दिन'
    },
    payment_steps: {
        '1': 'मस्टर रोल बंद से डाटा एंट्री का समय',
        '2': 'डाटा एंट्री से वेज लिस्ट बनाने का समय',
        '3': 'वेज लिस्ट बनाने से वेज लिस्ट भेजने का समय',
        '4': 'वेज लिस्ट भेजने से FTO बनाने का समय',
        '5': 'FTO बनाने से पहले हस्ताक्षर का समय',
        '6': 'पहले हस्ताक्षर से दूसरे हस्ताक्षर का समय',
        '7': 'दूसरे हस्ताक्षर से बैंक की कारवाई के समापन का समय'
    },
    payment_steps_labels: [
        'मस्टर रोल बंद से डाटा एंट्री का समय',
        'डाटा एंट्री से वेज लिस्ट बनाने का समय',
        'वेज लिस्ट बनाने से वेज लिस्ट भेजने का समय',
        'वेज लिस्ट भेजने से FTO बनाने का समय',
        'FTO बनाने से पहले हस्ताक्षर का समय',
        'पहले हस्ताक्षर से दूसरे हस्ताक्षर का समय',
        'दूसरे हस्ताक्षर से बैंक की कारवाई के समापन का समय'
    ],
    compare_chart_labels:{
        'state': 'राज्य औसत',
        'district': 'ज़िला औसत',
        'block': 'प्रखंड/जनपद औसत'
    },
    y_axis_labels: 'प्रक्रिया पूरी करने में लगे दिन',
    total_trans :"कुल लेनदेन",
    performance: {
        overview: {
            chart_a: {
                title: {
                    $filter: 'role',
                    district: 'आपके ज़िले का प्रदर्शन',
                    block: 'आपके प्रखंड/जनपद का प्रदर्शन',
                    $default: 'अवलोकन प्रदर्शन'
                },
                description: {
                    $filter: 'role',
                    district: 'आपके ज़िले में मज़दूरी भुगतान के हर एक पड़ाव पर लगे औसत दिन',
                    block: 'आपके प्रखंड/जनपद में मज़दूरी भुगतान के हर एक पड़ाव पर लगे औसत दिन',
                    $default: 'आपके क्षेत्रों में मज़दूरी भुगतान के हर एक पड़ाव पर लगे औसत दिन'
                }
            },
            chart_b: {
                title: {
                    $filter: 'role',
                    district: 'आपके प्रदर्शन की तुलना',
                    block: 'आपके प्रदर्शन की तुलना',
                    $default: 'आपके प्रदर्शन की तुलना'
                },
                description: {
                    $filter: 'role',
                    district: 'अपने ज़िले के औसत प्रदर्शन की तुलना राज्य के औसत प्रदर्शन से करें',
                    block: 'अपने प्रखंड/जनपद के औसत प्रदर्शन की तुलना ज़िले और राज्य के औसत प्रदर्शन से करें ',
                    $default: 'अपने प्रखंड/जनपद के औसत प्रदर्शन की तुलना अन्य क्षेत्रों के औसत प्रदर्शन से करें'
                }
            },
            tooltip: 'यह ग्राफ़ MGNREGA मज़दूरी भुगतान में लगा औसत समय दिखाता है| वर्णित तिथियों पर हुए भुगतान में लगे समय को 7 पड़ावों में बाटा गया है| इसलिए, केवल पूरे हुए भुगतान का डाटा दिखाया जा रहा है|',
        },
        discrete:{
            sub_heading: {
                '1' : 'आपकी पंचायत',
                '2' : 'का प्रदर्शन'
            },
            subtitle: {
                $filter: 'role',
                block: 'आपकी पंचायत में मज़दूरी भुगतान की प्रक्रिया के हर एक पड़ाव में लगा औसत समय',
                district: 'The performance of your blocks on average days to complete each step of the payment process.',
                $default :'अपने क्षेत्रों में मज़दूरी भुगतान की प्रक्रिया के हर एक पड़ाव में लगा औसत समय'
            },
            tooltip: 'यह ग्राफ़ MGNREGA मज़दूरी भुगतान में लगा औसत समय दिखाता है| वर्णित तिथियों पर हुए भुगतान में लगे समय को 7 पड़ावों में बाटा गया है| इसलिए, केवल पूरे हुए भुगतान का डाटा दिखाया जा रहा है|',
            ta_message: 'आपके प्रखंड/जनपद में कुछ TAs/SEs/JEs और उनकी पंचायतों के नाम MGNREGA वेबसाइट पर अपडेट नहीं किए गये हैं| इस के परिणाम स्वरूप, हम आपको आपके प्रखंड/जनपद के सभी TAs/SEs/JEs  के प्रदर्शन की जानकारी नहीं दे सकते| यह जानकारी पाने के लिए कृपया nrega.nic.in पर सभी TAs/SEs/JEs  और उनकी पंचायतों के नाम भरें|',
            grs_message: 'आपके प्रखंड/जनपद में कुछ GRSs और उनकी पंचायतों के नाम MGNREGA वेबसाइट पर अपडेट नहीं किए गये हैं| इस के परिणाम स्वरूप, हम आपको आपके प्रखंड/जनपद के सभी GRSs के प्रदर्शन की जानकारी नहीं दे सकते| यह जानकारी पाने के लिए कृपया nrega.nic.in पर सभी GRSs और उनकी पंचायतों के नाम भरें|',
            grouping_selectors: {
                no: 'बिना कोई वर्गीकरण',
                ta:'वर्ग: उप यंत्री',
                grs: 'वर्ग: GRS'
            },
            panchayat_chart_placeholder: 'भुगतान के प्रदर्शन को देखने के लिए एक पंचायत का चयन करें',
        }
    },
    musters: {
        current: {
            title: 'आज बंद हो रहे मस्टर्स'
        },
        delayed:{
            title : 'विलंबित मस्टर्स',
            t_2 : 'अटेंडेन्स नहीं भरी गयी (T+2)',
            t_5: 'MB नहीं भरी गयी (T+5)',
            t_6 : 'वेज लिस्ट नहीं भेजी गयी (T+6)',
            t_7 :'FTO पर पहला हस्ताक्षर नहीं हुआ (T+7)',
            T_8 :'FTO पर दूसरा हस्ताक्षर नहीं हुआ (T+8)'
        }
    }
};
<|endoftext|>


<commit_before>

$(function() {
	var dialDefaults = {
		// none so far
	}
});

function polar2rect(radius, angle, offset)
{
	if(offset==null) offset={x:0, y:0};
	angle += -Math.PI/2;
	return {
		x: offset.x + radius*Math.cos(angle),
		y: offset.y + radius*Math.sin(angle)
	};
}

var __glyphs = [
		"M -20 10 L 0 -10 L 20 10",
		"M -20 -10 L 0 10 L 20 -10"
	]
function glyph(id, parent, radius, angle, scale, offset)
{
	var pos = polar2rect(radius, angle, offset)
	return parent.append("path")
		.attr("class","glyph")
		.attr("d",__glyphs[id])
		.attr("stroke-width","4")
		.attr("transform","translate("+pos.x+","+pos.y+") "+((scale!=1.0)?" scale("+scale+")":""));
}

function Dial(_container)
{

	this.container = $(_container);

	//var svgbase = '<g class="dial"><g class="ticks"><g class="background-ticks" /></g></g>';
	//dial.svgroot.html( svgbase );
	var data = [10,20,30,40,50];
	var extents = this.extents = {width: 640, height: 640 };
	var width = this.width = 550;
	var height = this.height = 450;
	var radius = this.radius = extents.height/2;
	var center = this.center = {x: extents.width - radius, y: extents.height/2 };

	this.container.html("");
	var svg = d3.select(this.container[0]).append("svg")
    	.attr("width", this.width)
    	.attr("height", this.height)
		.attr("viewBox","0 0 "+extents.width+" "+extents.height);

	// add a border
	radius -= 32;

	var defs = svg.append("defs");
	var filter = defs.append("filter")
			.attr("id","fe3")
			.attr("x","0")
			.attr("y","0")
			.attr("width","200%")
			.attr("height","200%");
	filter.append("feOffset")
		.attr("result","offOut")
		.attr("in","SourceAlpha")
		.attr("dx","2")
		.attr("dy","2");
	filter.append("feGaussianBlur")
		.attr("result","blurOut")
		.attr("in","offOut")
		.attr("stdDeviation","10");
	filter.append("feBlend")
		.attr("in","SourceGraphic")
		.attr("in2","blurOut")
		.attr("mode","normal");
	filter.append("feComponentTransfer")
		.append("feFuncA")
			.attr("type","linear")
			.attr("slope","0.4");

	var bg = svg.append("g")
		.attr("class","background")
	bg.append("circle")
		.attr("class","background-circle")
		.attr("cx",center.x)
		.attr("cy",center.y)
		.attr("r",radius)
		.attr("filter","url(#fe3)")
		;

	var ticks = svg.append("g")
		.attr("class","ticks");
	var bgticks = ticks.append("g")
		.attr("class","background-ticks");
	var indicators = ticks.append("g")
		.attr("class","indicators");
	var buttons = ticks.append("g")
		.attr("class","buttons");

	// give radius positions of control parts
	this.radii = {
		ticks : { inner:radius-radius*0.24, outer:radius-5 }
	};

	var N=160;
	var dxAngle = 2*Math.PI/N;
	this.arcs = {};
	var tickArc = this.arcs.ticks = d3.svg.arc()
		.startAngle(function(d,h) { return d-h; })
		.endAngle(function(d,h) { return d+h; })
		.innerRadius(this.radii.ticks.inner)
		.outerRadius(this.radii.ticks.outer);
	var bigTickArc = d3.svg.arc()
		.startAngle(function(d,h) { return d-h*3; })
		.endAngle(function(d,h) { return d+h*3; })
		.innerRadius(radius-radius*0.24)
		.outerRadius(radius-5);
	var currentSpeedArc = d3.svg.arc()
		.startAngle(function(d,h) { return d-h*6; })
		.endAngle(function(d,h) { return d+h*6; })
		.innerRadius(radius-radius*0.25)
		.outerRadius(radius);
	var innerButtonArc = d3.svg.arc()
		.startAngle(function(a,h) { return a; })
		.endAngle(function(a,h) { return h; })
		.innerRadius(radius-radius*0.28)
		.outerRadius(radius);
	var outerButtonArc = d3.svg.arc()
		.startAngle(function(a,h) { return a; })
		.endAngle(function(a,h) { return h; })
		.innerRadius(radius+20)
		.outerRadius(radius+radius*0.17);

	var ang = Math.PI*0.82;
	var divisor = 9;
	var divisorAngle = this.divisorAngle = ang*2 / divisor;
	var n=0;
	for(a=-ang; a<ang; a+=dxAngle)
	{
		var _n = Math.floor((a+ang) / divisorAngle);
		bgticks.append("path")
			.attr("d", (_n==n) ? tickArc(a, dxAngle*0.1) : bigTickArc(a, dxAngle*0.1))
			.attr("transform","translate("+center.x+","+center.y+")");
		n = _n;
	}
	for(d=2; d<=8; d++)
	{
		var a = ((d-divisor/2 - 0.5)*divisorAngle )*180/Math.PI;
		bgticks.append("text")
			.attr("class","dial-ordinals")
			.attr("text-anchor","middle")
			.attr("x",center.x)
			.attr("y",center.y-radius*0.80)
			.attr("transform","rotate("+a+","+center.x+","+center.y+")")
			.text(d);
	}
	this.defaultTranslate="translate("+center.x+","+center.y+")";
	indicators.append("path")
		.attr("class","current-speed-indicator")
		.attr("d", currentSpeedArc(-ang, dxAngle*0.1) )
		.attr("transform",this.defaultTranslate+" rotate("+(this.divisorAngle*180/Math.PI)+")");

	// inner buttons - faster/slower
	buttons.append("path")
		.attr("id","speed-increase")
		.attr("class","inner speed-increase")
		.attr("d", innerButtonArc(ang+dxAngle, Math.PI-0.01))
		.attr("transform","translate("+center.x+","+center.y+")");
	buttons.append("path")
		.attr("id","speed-decrease")
		.attr("class","inner speed-decrease")
		.attr("d", innerButtonArc(Math.PI+0.01, 2*Math.PI-ang-dxAngle))
		.attr("transform","translate("+center.x+","+center.y+")");
	// button glyphs
	glyph(0, buttons, radius*0.87, Math.PI-(Math.PI-ang)/2, 1.0, center);
	glyph(1, buttons, radius*0.87, Math.PI+(Math.PI-ang)/2, 1.0, center);

	// outer buttons - incline/decline
	var incdec_rotation = 1.35;
	var incdec_width = 0.03;
	buttons.append("path")
		.attr("class","incline-background")
		.attr("d", outerButtonArc(1.2*Math.PI, 1.65*Math.PI))
		.attr("transform","translate("+center.x+","+center.y+")");
	buttons.append("path")
		.attr("class","incline-indicator")
		.attr("d", outerButtonArc((incdec_rotation-incdec_width)*Math.PI, incdec_rotation*Math.PI-0.01))
		.attr("transform","translate("+center.x+","+center.y+")");

	// speed indicator
	var status = svg.append("g")
		.attr("class","status");
	status.append("text")
		.attr("class","status-indicator")
		.attr("text-anchor","middle")
		.attr("x",center.x)
		.attr("y",center.y-140)
		.text("...");
	status.append("text")
		.attr("class","running-time")
		.attr("text-anchor","middle")
		.attr("x",center.x)
		.attr("y",center.y-100)
		.text("0:00");
	status.append("text")
		.attr("class","speed-indicator")
		.attr("text-anchor","middle")
		.attr("x",center.x)
		.attr("y",center.y+70)
		.text("");

	// goal indicator
	var goalAngle = { begin: 2*Math.PI-ang-dxAngle, end: 2*Math.PI+ang+dxAngle };
	this.arcs.goal = d3.svg.arc()
		.startAngle(function(a,h) { return goalAngle.begin; })
		.endAngle(function(a,h) { return goalAngle.begin + (goalAngle.end-goalAngle.begin)*h; })
		.innerRadius(radius-radius*0.245-10)
		.outerRadius(radius-radius*0.245-5);
	indicators.append("path")
		.attr("id","goal")
		.attr("class","goal")
		.attr("d", this.arcs.goal(0, 0.25))
		.attr("transform","translate("+center.x+","+center.y+")");

	var dial = this;
	this.container.on("click",function(){
		dial.click(event.layerX, event.layerY);
	});
}

Dial.prototype.setSpeed = function(speed)
{
	var ctrl = this.container.find(".indicators .current-speed-indicator");
	if(speed>2)
	{
		var val = (speed-1) * (this.divisorAngle*180/Math.PI);
		ctrl.attr("style","");
		ctrl.attr("transform",this.defaultTranslate+" rotate("+val+")");
	} else {
		ctrl.attr("style","display:none");
	}
}

Dial.prototype.setRunningTime = function(seconds,minutes,hours)
{
	var percent = Math.min(1.0, (seconds+minutes*60+hours*3600) / this.treadmill.goaltime);
	this.container.find(".indicators .goal")
		.attr("d", this.arcs.goal(0, percent))

}

Dial.prototype.click = function(x,y)
{
	var scale = {
		x: (this.width / this.extents.width),
		y: (this.height / this.extents.height)
	};
	var x1=x/scale.x - this.center.x,
	    y1=y/scale.y - this.center.y;
	var angle = Math.atan2(y1,x1),
	    radius = Math.sqrt(x1*x1 + y1*y1);
	console.log("click @ "+x1+":"+y1+"   a:"+(angle*180/Math.PI)+"  r:"+radius);
	if(radius > this.radii.ticks.inner && radius < this.radii.ticks.outer) {
		console.log("speed "+angle+"  "+this.radii.ticks.inner+" < "+radius+" < "+this.radii.ticks.outer);
	}
}

// extend the jQuery class so we can easily create a dial in a control just by calling dial()
jQuery.fn.extend({
    	dial: function() {
    		//return this.each(function() {
    			var dial = new Dial($(this));
				return dial;
			//});
		}
	});
<commit_msg>fixed d3.svg.arc() to d3.arc() due to new D3 API change<commit_after>

$(function() {
	var dialDefaults = {
		// none so far
	}
});

function polar2rect(radius, angle, offset)
{
	if(offset==null) offset={x:0, y:0};
	angle += -Math.PI/2;
	return {
		x: offset.x + radius*Math.cos(angle),
		y: offset.y + radius*Math.sin(angle)
	};
}

var __glyphs = [
		"M -20 10 L 0 -10 L 20 10",
		"M -20 -10 L 0 10 L 20 -10"
	]
function glyph(id, parent, radius, angle, scale, offset)
{
	var pos = polar2rect(radius, angle, offset)
	return parent.append("path")
		.attr("class","glyph")
		.attr("d",__glyphs[id])
		.attr("stroke-width","4")
		.attr("transform","translate("+pos.x+","+pos.y+") "+((scale!=1.0)?" scale("+scale+")":""));
}

function Dial(_container)
{

	this.container = $(_container);

	//var svgbase = '<g class="dial"><g class="ticks"><g class="background-ticks" /></g></g>';
	//dial.svgroot.html( svgbase );
	var data = [10,20,30,40,50];
	var extents = this.extents = {width: 640, height: 640 };
	var width = this.width = 550;
	var height = this.height = 450;
	var radius = this.radius = extents.height/2;
	var center = this.center = {x: extents.width - radius, y: extents.height/2 };

	this.container.html("");
	var svg = d3.select(this.container[0]).append("svg")
    	.attr("width", this.width)
    	.attr("height", this.height)
		.attr("viewBox","0 0 "+extents.width+" "+extents.height);

	// add a border
	radius -= 32;

	var defs = svg.append("defs");
	var filter = defs.append("filter")
			.attr("id","fe3")
			.attr("x","0")
			.attr("y","0")
			.attr("width","200%")
			.attr("height","200%");
	filter.append("feOffset")
		.attr("result","offOut")
		.attr("in","SourceAlpha")
		.attr("dx","2")
		.attr("dy","2");
	filter.append("feGaussianBlur")
		.attr("result","blurOut")
		.attr("in","offOut")
		.attr("stdDeviation","10");
	filter.append("feBlend")
		.attr("in","SourceGraphic")
		.attr("in2","blurOut")
		.attr("mode","normal");
	filter.append("feComponentTransfer")
		.append("feFuncA")
			.attr("type","linear")
			.attr("slope","0.4");

	var bg = svg.append("g")
		.attr("class","background")
	bg.append("circle")
		.attr("class","background-circle")
		.attr("cx",center.x)
		.attr("cy",center.y)
		.attr("r",radius)
		.attr("filter","url(#fe3)")
		;

	var ticks = svg.append("g")
		.attr("class","ticks");
	var bgticks = ticks.append("g")
		.attr("class","background-ticks");
	var indicators = ticks.append("g")
		.attr("class","indicators");
	var buttons = ticks.append("g")
		.attr("class","buttons");

	// give radius positions of control parts
	this.radii = {
		ticks : { inner:radius-radius*0.24, outer:radius-5 }
	};

	var N=160;
	var dxAngle = 2*Math.PI/N;
	this.arcs = {};
	var tickArc = this.arcs.ticks = d3.arc()
		.startAngle(function(d,h) { return d-h; })
		.endAngle(function(d,h) { return d+h; })
		.innerRadius(this.radii.ticks.inner)
		.outerRadius(this.radii.ticks.outer);
	var bigTickArc = d3.arc()
		.startAngle(function(d,h) { return d-h*3; })
		.endAngle(function(d,h) { return d+h*3; })
		.innerRadius(radius-radius*0.24)
		.outerRadius(radius-5);
	var currentSpeedArc = d3.arc()
		.startAngle(function(d,h) { return d-h*6; })
		.endAngle(function(d,h) { return d+h*6; })
		.innerRadius(radius-radius*0.25)
		.outerRadius(radius);
	var innerButtonArc = d3.arc()
		.startAngle(function(a,h) { return a; })
		.endAngle(function(a,h) { return h; })
		.innerRadius(radius-radius*0.28)
		.outerRadius(radius);
	var outerButtonArc = d3.arc()
		.startAngle(function(a,h) { return a; })
		.endAngle(function(a,h) { return h; })
		.innerRadius(radius+20)
		.outerRadius(radius+radius*0.17);

	var ang = Math.PI*0.82;
	var divisor = 9;
	var divisorAngle = this.divisorAngle = ang*2 / divisor;
	var n=0;
	for(a=-ang; a<ang; a+=dxAngle)
	{
		var _n = Math.floor((a+ang) / divisorAngle);
		bgticks.append("path")
			.attr("d", (_n==n) ? tickArc(a, dxAngle*0.1) : bigTickArc(a, dxAngle*0.1))
			.attr("transform","translate("+center.x+","+center.y+")");
		n = _n;
	}
	for(d=2; d<=8; d++)
	{
		var a = ((d-divisor/2 - 0.5)*divisorAngle )*180/Math.PI;
		bgticks.append("text")
			.attr("class","dial-ordinals")
			.attr("text-anchor","middle")
			.attr("x",center.x)
			.attr("y",center.y-radius*0.80)
			.attr("transform","rotate("+a+","+center.x+","+center.y+")")
			.text(d);
	}
	this.defaultTranslate="translate("+center.x+","+center.y+")";
	indicators.append("path")
		.attr("class","current-speed-indicator")
		.attr("d", currentSpeedArc(-ang, dxAngle*0.1) )
		.attr("transform",this.defaultTranslate+" rotate("+(this.divisorAngle*180/Math.PI)+")");

	// inner buttons - faster/slower
	buttons.append("path")
		.attr("id","speed-increase")
		.attr("class","inner speed-increase")
		.attr("d", innerButtonArc(ang+dxAngle, Math.PI-0.01))
		.attr("transform","translate("+center.x+","+center.y+")");
	buttons.append("path")
		.attr("id","speed-decrease")
		.attr("class","inner speed-decrease")
		.attr("d", innerButtonArc(Math.PI+0.01, 2*Math.PI-ang-dxAngle))
		.attr("transform","translate("+center.x+","+center.y+")");
	// button glyphs
	glyph(0, buttons, radius*0.87, Math.PI-(Math.PI-ang)/2, 1.0, center);
	glyph(1, buttons, radius*0.87, Math.PI+(Math.PI-ang)/2, 1.0, center);

	// outer buttons - incline/decline
	var incdec_rotation = 1.35;
	var incdec_width = 0.03;
	buttons.append("path")
		.attr("class","incline-background")
		.attr("d", outerButtonArc(1.2*Math.PI, 1.65*Math.PI))
		.attr("transform","translate("+center.x+","+center.y+")");
	buttons.append("path")
		.attr("class","incline-indicator")
		.attr("d", outerButtonArc((incdec_rotation-incdec_width)*Math.PI, incdec_rotation*Math.PI-0.01))
		.attr("transform","translate("+center.x+","+center.y+")");

	// speed indicator
	var status = svg.append("g")
		.attr("class","status");
	status.append("text")
		.attr("class","status-indicator")
		.attr("text-anchor","middle")
		.attr("x",center.x)
		.attr("y",center.y-140)
		.text("...");
	status.append("text")
		.attr("class","running-time")
		.attr("text-anchor","middle")
		.attr("x",center.x)
		.attr("y",center.y-100)
		.text("0:00");
	status.append("text")
		.attr("class","speed-indicator")
		.attr("text-anchor","middle")
		.attr("x",center.x)
		.attr("y",center.y+70)
		.text("");

	// goal indicator
	var goalAngle = { begin: 2*Math.PI-ang-dxAngle, end: 2*Math.PI+ang+dxAngle };
	this.arcs.goal = d3.arc()
		.startAngle(function(a,h) { return goalAngle.begin; })
		.endAngle(function(a,h) { return goalAngle.begin + (goalAngle.end-goalAngle.begin)*h; })
		.innerRadius(radius-radius*0.245-10)
		.outerRadius(radius-radius*0.245-5);
	indicators.append("path")
		.attr("id","goal")
		.attr("class","goal")
		.attr("d", this.arcs.goal(0, 0.25))
		.attr("transform","translate("+center.x+","+center.y+")");

	var dial = this;
	this.container.on("click",function(){
		dial.click(event.layerX, event.layerY);
	});
}

Dial.prototype.setSpeed = function(speed)
{
	var ctrl = this.container.find(".indicators .current-speed-indicator");
	if(speed>2)
	{
		var val = (speed-1) * (this.divisorAngle*180/Math.PI);
		ctrl.attr("style","");
		ctrl.attr("transform",this.defaultTranslate+" rotate("+val+")");
	} else {
		ctrl.attr("style","display:none");
	}
}

Dial.prototype.setRunningTime = function(seconds,minutes,hours)
{
	var percent = Math.min(1.0, (seconds+minutes*60+hours*3600) / this.treadmill.goaltime);
	this.container.find(".indicators .goal")
		.attr("d", this.arcs.goal(0, percent))

}

Dial.prototype.click = function(x,y)
{
	var scale = {
		x: (this.width / this.extents.width),
		y: (this.height / this.extents.height)
	};
	var x1=x/scale.x - this.center.x,
	    y1=y/scale.y - this.center.y;
	var angle = Math.atan2(y1,x1),
	    radius = Math.sqrt(x1*x1 + y1*y1);
	console.log("click @ "+x1+":"+y1+"   a:"+(angle*180/Math.PI)+"  r:"+radius);
	if(radius > this.radii.ticks.inner && radius < this.radii.ticks.outer) {
		console.log("speed "+angle+"  "+this.radii.ticks.inner+" < "+radius+" < "+this.radii.ticks.outer);
	}
}

// extend the jQuery class so we can easily create a dial in a control just by calling dial()
jQuery.fn.extend({
    	dial: function() {
    		//return this.each(function() {
    			var dial = new Dial($(this));
				return dial;
			//});
		}
	});
<|endoftext|>


<commit_before>/*global describe*/
/*global require*/
/*global it*/
/*global console*/
/*global process*/

var problems = require("../test/all_problems.json"),
    fs = require("fs"),
    solver = require("../src/solver");

// var bigMILP = require('./test_suite/SPY_SPY_SPY_20170616.json');

// bigMILP.name = 'SPY_SPY_SPY_20170616';
// delete bigMILP.ints;
// problems.push(bigMILP);

console.log("------------------------");
console.log("-FORWARD-");
console.log("------------------------");

var log = {};

for (var i = 0; i < problems.length; i++) {

    var k = 0,
        j = problems[i];

        log[j.name] = {};

        for(var constraint in j.constraints){
            if(j.constraints[constraint].max){
                log[j.name].constraints = log[j.name].constraints  || 0;
                log[j.name].constraints++;
            }

            if(j.constraints[constraint].min){
                log[j.name].constraints = log[j.name].constraints  || 0;
                log[j.name].constraints++;
            }
        }

        log[j.name].variables = Object.keys(j.variables).length;

        if(j.ints){
            log[j.name].ints = Object.keys(j.ints).length;
        }

}

var totalTime = 0;

// var nProblems = problems.length;
// var currentProblem = 0;

// function RunNextTest() {
//     var problemExecuted = false;
//     var problem = problems[currentProblem];
//     // if (problem.name === 'Monster Problem') {
//     if (problem.name !== 'monster_II') {
//         var date_0 = process.hrtime();
//         var d = solver.Solve(problem);
//         var a = process.hrtime(date_0);

//         log[problem.name].result = d.result;
//         log[problem.name].time =  a[0] + a[1] / 1e9;
//         log[problem.name].iter = d.iter ? d.iter : undefined;
//         log[problem.name].solution = d;

//         totalTime += a[0] + a[1] / 1e9;
//         problemExecuted = true;
//     }

//     currentProblem += 1;
//     if (currentProblem < nProblems) {
//         if (problemExecuted === true) {
//             setTimeout(RunNextTest, 2000);
//         } else {
//             RunNextTest();
//         }
//     } else {
//         // This is the end
//         console.log(log);
//         console.log('All Solved in', totalTime, 'seconds');
//     }
// }

// RunNextTest();

for(i = 0; i < problems.length; i++) {
// for(i = problems.length - 1; i >= 0; i--){
    problem = problems[i];

    // if (problem.name === 'monster_II') {
    // if (problem.name !== 'SPY_SPY_SPY_20170616') {
    // if (problem.name !== 'monster_II') {
    // if (problem.name !== 'Monster Problem') {
    // if (problem.name === 'monster_II' || problem.name === 'Monster Problem') {
    // if (problem.name !== 'Berlin Air Lift Problem') {
    // if (problem.name !== 'Integer Berlin Air Lift Problem') {
    // if (problem.name !== 'Infeasible Berlin Air Lift Problem') {
    // if (problem.name !== 'Generic Business Problem 2') {
    // if (problem.name !== 'Wiki 1') {
    // if (problem.name !== 'Artificial') {
    // if (problem.name !== 'Coffee Problem') {
    // if (problem.name !== 'Shift Work Problem') {
    // if (problem.name !== 'Degenerate Max') {
    // if (problem.name !== 'Degenerate Min') {
    if (problem.name !== 'Integer Wood Problem') {
    // if (problem.name !== 'Chevalier 1') {
    // if (problem.name !== 'Integer Chocolate Problem') {
    // if (problem.name !== 'Integer Clothing Shop Problem II') {
        continue;
    }
console.log('>>>>> Solving', problem.name, '<<<<<');
    var date_0 = process.hrtime();
    var d = solver.Solve(problem);
    var a = process.hrtime(date_0);

    log[problem.name].objectiveValue = d.objectiveValue;
    log[problem.name].time =  a[0] + a[1] / 1e9;
    log[problem.name].iter = d.iter ? d.iter : undefined;
    log[problem.name].solutionSet = d.solutionSet;
    log[problem.name].feasible = d.feasible;
// console.log('Result', d.result);
// console.log('>>>>> Solved in', (a[0] + a[1] / 1e9) * 1000, 'ms <<<<<');
// console.log('');

    totalTime += a[0] + a[1] / 1e9;
}

console.log(log);
console.log('All Solved in', totalTime, 'seconds');<commit_msg>* Undid changes to solver.play file<commit_after>/*global describe*/
/*global require*/
/*global it*/
/*global console*/
/*global process*/

var problems = require("../test/all_problems.json"),
    fs = require("fs"),
    solver = require("../src/solver");

console.log("------------------------");
console.log("-FORWARD-");
console.log("------------------------");

var log = {};

for (var i = 0; i < problems.length; i++) {

    var k = 0,
        j = problems[i];

        log[j.name] = {};

        for(var constraint in j.constraints){
            if(j.constraints[constraint].max){
                log[j.name].constraints = log[j.name].constraints  || 0;
                log[j.name].constraints++;
            }

            if(j.constraints[constraint].min){
                log[j.name].constraints = log[j.name].constraints  || 0;
                log[j.name].constraints++;
            }
        }

        log[j.name].variables = Object.keys(j.variables).length;

        if(j.ints){
            log[j.name].ints = Object.keys(j.ints).length;
        }

}

for( i = 0; i < problems.length; i++){
    j = problems[i];
    var date_0 = process.hrtime();
    var d = solver.Solve(j);
    var a = process.hrtime(date_0);

    log[j.name].result = d.result;
    log[j.name].time =  a[0] + a[1] / 1e9;
    log[j.name].iter = d.iter ? d.iter : undefined;

}

console.log(log);<|endoftext|>


<commit_before>// ==UserScript==
// @name Debian package cross link
// @description Link between Debian and Ubuntu package page
// @namespace https://franklinyu.github.io
// @version 0.1.0
// @match https://packages.debian.org/*
// @exclude https://packages.debian.org/about/*
// @exclude https://packages.debian.org/search?*
// @exclude https://packages.debian.org/*/filelist
// @exclude https://packages.debian.org/*/download
// @match https://packages.ubuntu.com/*
// @exclude https://packages.ubuntu.com/about/*
// @exclude https://packages.ubuntu.com/search?*
// @exclude https://packages.ubuntu.com/*/filelist
// @exclude https://packages.ubuntu.com/*/download
// @grant none
// ==/UserScript==

let url
let text
if (location.hostname === 'packages.debian.org') {
	url = new URL('https://packages.ubuntu.com/search')
	text = 'Ubuntu'
} else {
	url = new URL('https://packages.debian.org/search')
	text = 'Debian'
}
url.searchParams.append('exact', 1)

if (location.pathname.startsWith('/source/')) {
	const package = location.pathname.split('/')[3]
	if (!package)
		throw location.pathname
	url.searchParams.append('keywords', package)
	url.searchParams.append('searchon', 'sourcenames')
} else {
	const package = location.pathname.split('/')[2]
	if (!package)
		throw location.pathname
	url.searchParams.append('keywords', package)
}

const anchor = document.createElement('a')
anchor.href = url
anchor.innerText = text
document.getElementById('pothers').innerHTML += ` [ ${anchor.outerHTML} ]`
<commit_msg>Rename script<commit_after>// ==UserScript==
// @name Ubuntu/Debian package cross link
// @description Link between Debian and Ubuntu package page
// @namespace https://franklinyu.github.io
// @version 0.1.0
// @match https://packages.debian.org/*
// @exclude https://packages.debian.org/about/*
// @exclude https://packages.debian.org/search?*
// @exclude https://packages.debian.org/*/filelist
// @exclude https://packages.debian.org/*/download
// @match https://packages.ubuntu.com/*
// @exclude https://packages.ubuntu.com/about/*
// @exclude https://packages.ubuntu.com/search?*
// @exclude https://packages.ubuntu.com/*/filelist
// @exclude https://packages.ubuntu.com/*/download
// @grant none
// ==/UserScript==

let url
let text
if (location.hostname === 'packages.debian.org') {
	url = new URL('https://packages.ubuntu.com/search')
	text = 'Ubuntu'
} else {
	url = new URL('https://packages.debian.org/search')
	text = 'Debian'
}
url.searchParams.append('exact', 1)

if (location.pathname.startsWith('/source/')) {
	const package = location.pathname.split('/')[3]
	if (!package)
		throw location.pathname
	url.searchParams.append('keywords', package)
	url.searchParams.append('searchon', 'sourcenames')
} else {
	const package = location.pathname.split('/')[2]
	if (!package)
		throw location.pathname
	url.searchParams.append('keywords', package)
}

const anchor = document.createElement('a')
anchor.href = url
anchor.innerText = text
document.getElementById('pothers').innerHTML += ` [ ${anchor.outerHTML} ]`
<|endoftext|>


<commit_before>"use strict";

var os = require('os');
var Q = require('q');
var scatteredStore = require('..');
var jetpack = require('fs-jetpack');

var path = os.tmpdir() + '/scattered-store-benchmark';

var numberOfOperations = 20000;
var itemSize = 1024 * 50;

var store;
var startTime;
var keys = [];
var testObj = new Buffer(itemSize);

var generateKey = function () {
    var key = "key" + keys.length.toString();
    keys.push(key);
    return key;
}

var start = function (message) {
    process.stdout.write(message);
    startTime = Date.now();
}

var stop = function () {
    var endTime = Date.now();
    var duration = (endTime - startTime) / 1000;
    var opsPerSec = Math.round(numberOfOperations / duration);
    console.log(' ' + opsPerSec + " ops/s");
}

// clean before benchmark
jetpack.dir(path, { exists: false });

console.log('Testing scattered-store performance: ' + numberOfOperations +
            ' items, ' + (itemSize / 1024) + 'KB each, ' +
            Math.round(numberOfOperations * itemSize / (1024 * 1024)) +
            'MB combined.');

scatteredStore.create(path)
.then(function (createdStore) {
    store = createdStore;

    start('set...');

    for (var i = 0; i < numberOfOperations; i += 1) {
        store.set(generateKey(), testObj)
    }

    // order of operations is preserved,
    // so we know that after finish of this one all are finished
    return store.set(generateKey(), testObj);
})
.then(function () {

    stop();
    start('get...');

    for (var i = 0; i < keys.length; i += 1) {
        store.get(keys[i])
    }

    return store.get("none");
})
.then(function () {

    stop();
    start('all...');

    var deferred = Q.defer();

    var stream = store.all()
    .on('readable', function () {
        stream.read();
    })
    .on('end', deferred.resolve);

    return deferred.promise;
})
.then(function () {

    stop();
    start('del...');

    for (var i = 0; i < keys.length; i += 1) {
        store.del(keys[i])
    }

    return store.del("none");
})
.then(function () {

    stop();

    // clean after benchmark
    jetpack.dir(path, { exists: false });
});
<commit_msg>Benchmark works again<commit_after>"use strict";

var os = require('os');
var Q = require('q');
var scatteredStore = require('..');
var jetpack = require('fs-jetpack');

var path = os.tmpdir() + '/scattered-store-benchmark';

var numberOfOperations = 20000;
var itemSize = 1024 * 50;

var startTime;
var keys = [];
var testObj = new Buffer(itemSize);

var generateKey = function () {
    var key = "key" + keys.length.toString();
    keys.push(key);
    return key;
}

var start = function (message) {
    process.stdout.write(message);
    startTime = Date.now();
}

var stop = function () {
    var endTime = Date.now();
    var duration = (endTime - startTime) / 1000;
    var opsPerSec = Math.round(numberOfOperations / duration);
    console.log(' ' + opsPerSec + " ops/s");
}

// clean before benchmark
jetpack.dir(path, { exists: false });

console.log('Testing scattered-store performance: ' + numberOfOperations +
            ' items, ' + (itemSize / 1024) + 'KB each, ' +
            Math.round(numberOfOperations * itemSize / (1024 * 1024)) +
            'MB combined.');

var store = scatteredStore.create(path, function (err) {
    if (!err) {
        run();
    }
});

var run = function () {

    start('set...');

    for (var i = 0; i < numberOfOperations; i += 1) {
        store.set(generateKey(), testObj)
    }

    // order of operations is preserved,
    // so we know that after finish of this one all are finished
    store.set(generateKey(), testObj)
    .then(function () {

        stop();
        start('get...');

        for (var i = 0; i < keys.length; i += 1) {
            store.get(keys[i])
        }

        return store.get("none");
    })
    .then(function () {

        stop();
        start('getAll...');

        var deferred = Q.defer();

        var stream = store.getAll()
        .on('readable', function () {
            stream.read();
        })
        .on('end', deferred.resolve);

        return deferred.promise;
    })
    .then(function () {

        stop();
        start('delete...');

        for (var i = 0; i < keys.length; i += 1) {
            store.delete(keys[i])
        }

        return store.delete("none");
    })
    .then(function () {

        stop();

        // clean after benchmark
        jetpack.dir(path, { exists: false });
    });
};<|endoftext|>


<commit_before>var TsvParse = function (text) {
    var header;
    return d3.tsv.parseRows(text, function (row, i) {
        if (i) {
            var o = {}, j = -1, m = header.length;
            while (++j < m) {
                var rowvalue = row[j];
                if (_.isString(rowvalue)) rowvalue = rowvalue.trim();
                o[header[j]] = rowvalue;
            }
            return o;
        } else {
            header = _.map(row, function (k) {
                if (_.isString(k)) return k.trim();
                return k;
            });
            return null;
        }
    });
};

var BasicModel = Backbone.Model.extend({

    url: function () {
        return this.get("url");
    },

    parse: function (txt) {
        return { "items": TsvParse(txt) };
    },

    fetch: function (options) {
        return Backbone.Model.prototype.fetch.call(this, _.extend({dataType: "text"}, options));
    }
});

module.exports = Backbone.Model.extend({

    url: function () {
        return this.get_base_uri("nodes");
    },

    parse: function (txt) {
        var nodes = TsvParse(txt);

        var _this = this;
        var edges = new BasicModel({ "url": this.get_base_uri("edges") });
        edges.fetch({
            "async": false,
            "success": function () {
                _this.set("edges", edges.get("items"));
            }
        });

        var param_measure_types = [];
        if (_.has(this.attributes, "measure_type")) {
            param_measure_types = this.get(this.get("measure_type")) || [];
        }

        _.each(nodes, function (node) {
            node.uid = _.uniqueId("node_");
        });

        var measure_keys = [];
        var nodetypes = _.map(_.groupBy(nodes, "type"), function (group, type) {
            return {
                "label": type,
                "nodes": _.map(group, function (item) {
                    var targeted_keys = _.without(_.keys(item), "id", "type", "uid");
                    if (!_.isEmpty(param_measure_types)) targeted_keys = _.intersection(param_measure_types, targeted_keys);

                    return {
                        "uid": item["uid"],
                        "label": item["id"],
                        "measures": _.map(targeted_keys, function (key) {
                            measure_keys.push(key);
                            return { "key": key, "value": item[key] };
                        }, this)
                    };
                }, this)
            }
        }, this);

        return { "nodes": nodes, "nodesByType": nodetypes, "measureKeys": _.uniq(measure_keys) };
    },

    fetch: function (options) {
        return Backbone.Model.prototype.fetch.call(this, _.extend({dataType: "text"}, options));
    },

    get_base_uri: function (suffix) {
        var data_uri = this.get("data_uri");
        var dataset_id = this.get("dataset_id");
        return data_uri + "/" + this.get("catalog_unit")[suffix];
    }

});<commit_msg>changed keys<commit_after>var TsvParse = function (text) {
    var header;
    return d3.tsv.parseRows(text, function (row, i) {
        if (i) {
            var o = {}, j = -1, m = header.length;
            while (++j < m) {
                var rowvalue = row[j];
                if (_.isString(rowvalue)) rowvalue = rowvalue.trim();
                o[header[j]] = rowvalue;
            }
            return o;
        } else {
            header = _.map(row, function (k) {
                if (_.isString(k)) return k.trim();
                return k;
            });
            return null;
        }
    });
};

var BasicModel = Backbone.Model.extend({

    url: function () {
        return this.get("url");
    },

    parse: function (txt) {
        return { "items": TsvParse(txt) };
    },

    fetch: function (options) {
        return Backbone.Model.prototype.fetch.call(this, _.extend({dataType: "text"}, options));
    }
});

module.exports = Backbone.Model.extend({

    url: function () {
        return this.get_base_uri("nodes");
    },

    parse: function (txt) {
        var nodes = TsvParse(txt);

        var _this = this;
        var edges = new BasicModel({ "url": this.get_base_uri("edges") });
        edges.fetch({
            "async": false,
            "success": function () {
                _this.set("edges", edges.get("items"));
            }
        });

        var measured_values = [];
        if (_.has(this.attributes, "measured_values")) {
            measured_values = this.get(this.get("measured_values")) || [];
        }

        _.each(nodes, function (node) {
            node.uid = _.uniqueId("node_");
        });

        var measure_keys = [];
        var nodetypes = _.map(_.groupBy(nodes, "type"), function (group, type) {
            return {
                "label": type,
                "nodes": _.map(group, function (item) {
                    var targeted_keys = _.without(_.keys(item), "id", "type", "uid");
                    if (!_.isEmpty(measured_values)) targeted_keys = _.intersection(measured_values, targeted_keys);

                    return {
                        "uid": item["uid"],
                        "label": item["id"],
                        "measures": _.map(targeted_keys, function (key) {
                            measure_keys.push(key);
                            return { "key": key, "value": item[key] };
                        }, this)
                    };
                }, this)
            }
        }, this);

        return { "nodes": nodes, "nodesByType": nodetypes, "measureKeys": _.uniq(measure_keys) };
    },

    fetch: function (options) {
        return Backbone.Model.prototype.fetch.call(this, _.extend({dataType: "text"}, options));
    },

    get_base_uri: function (suffix) {
        var data_uri = this.get("data_uri");
        var dataset_id = this.get("dataset_id");
        return data_uri + "/" + this.get("catalog_unit")[suffix];
    }

});<|endoftext|>


<commit_before>var path = require('path');
var urlParser = require('url');
var http = require('http');
var https = require('https');
var querystring = require('querystring');

exports.register = function (plugin, options, next) {

  // Sends a request to Superfeedr: private sugar!
  function _superfeedrRequest(mode, url, id, opts, cb) {
    var post_data = {
      'hub.mode': mode,
      'hub.topic': url,
      'hub.callback': urlParser.format({
        'protocol': options.webhooks.scheme || "http",
        'hostname': options.webhooks.host,
        'port': options.webhooks.port,
        'pathname': path.join(options.webhooks.base, id)
      })
    };

    if(opts.retrieve) {
      post_data['retrieve'] = true;
    }
    if(opts.format) {
      post_data['format'] = opts.format;
    }
    if(opts.secret) {
      post_data['hub.secret'] = opts.secret;
    }

    var post_options = {
      'host': 'push.superfeedr.com',
      'port': '443',
      'path': '/',
      'auth': [options.login, options.password].join(':'),
      'method': 'POST',
      'headers': {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': querystring.stringify(post_data).length
      }
    };

    var post_req = https.request(post_options, function(res) {
      var data = '';
      res.setEncoding('utf8');
      res.on('data', function (chunk) {
        data += chunk;
      });
      res.on('end', function() {
        if(res.statusCode == 204 || res.statusCode == 200) {
          cb(null, data);
        }
        else {
          cb(new Error(res.statusCode, data));
        }
      })
    });

    post_req.write(querystring.stringify(post_data));
    post_req.end();
  }

  // Subscribe is called from anywhere in the hapi app like this
  // Params
  // - url: url of the feed/topic to which you want to subscribe
  // - id: unique id of the feed/topic uou want to subscribe. This is important to identify unique notifications. You should use some kind of primary key.
  // - opts:
  //      - retrieve: set to true if you want previous items from the feed
  //      - format: 'atom' or 'json'
  //      - secret: a unique secret used to compute an HMAC key for notifications.
  // - the callback will be called with the result of the subscription. First param is error, second param is body if retrieve:true.
  plugin.expose('subscribe', function(url, id, opts, cb) {
    _superfeedrRequest('subscribe', url, id, opts, cb);
  });

  // Unsubscribe is called from anywhere in the hapi app like this
  // Params
  // - url: url of the feed/topic to which you want to subscribe
  // - id: unique id of the feed/topic that you used to subscribe
  // - the callback will be called with the result of the subscription. First param is error, second param is body if retrieve:true.
  plugin.expose('unsubscribe', function(url, id, cb) {
    _superfeedrRequest('unsubscribe', url, id, {}, cb);
  });

  // Expose the events
  plugin.expose('on', plugin.events.on);

  plugin.route({
    method: 'POST',
    path: path.join(options.webhooks.base, '{feed_id?}'),
    handler: function(request, reply) {
      // The event is triggered so that anyone who listens to 'notification' on the superfeedr plugin (how do we do that?)
      // will recive the feed_id, the RAWBODY (if we are able to extract it), the topic/feed url and the request object
      // if they want to debug it.
      plugin.events.emit('notification', request.params.feed_id, request.payload, request.headers['x-pubsubhubbub-topic'], request);
      reply('Thanks Superfeedr');
    },
    config: {
      payload: {
        allow: ["application/atom+xml", "application/json"],
        parse: false
      }
    }
  });

  next();
}

exports.register.attributes = {
  multiple: false,
  name: 'superfeedr',
  version: '1.0.0'
};
<commit_msg>cannot expose just on, so exposing all of events<commit_after>var path = require('path');
var urlParser = require('url');
var http = require('http');
var https = require('https');
var querystring = require('querystring');

exports.register = function (plugin, options, next) {

  // Sends a request to Superfeedr: private sugar!
  function _superfeedrRequest(mode, url, id, opts, cb) {
    var post_data = {
      'hub.mode': mode,
      'hub.topic': url,
      'hub.callback': urlParser.format({
        'protocol': options.webhooks.scheme || "http",
        'hostname': options.webhooks.host,
        'port': options.webhooks.port,
        'pathname': path.join(options.webhooks.base, id)
      })
    };

    if(opts.retrieve) {
      post_data['retrieve'] = true;
    }
    if(opts.format) {
      post_data['format'] = opts.format;
    }
    if(opts.secret) {
      post_data['hub.secret'] = opts.secret;
    }

    var post_options = {
      'host': 'push.superfeedr.com',
      'port': '443',
      'path': '/',
      'auth': [options.login, options.password].join(':'),
      'method': 'POST',
      'headers': {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': querystring.stringify(post_data).length
      }
    };

    var post_req = https.request(post_options, function(res) {
      var data = '';
      res.setEncoding('utf8');
      res.on('data', function (chunk) {
        data += chunk;
      });
      res.on('end', function() {
        if(res.statusCode == 204 || res.statusCode == 200) {
          cb(null, data);
        }
        else {
          cb(new Error(res.statusCode, data));
        }
      })
    });

    post_req.write(querystring.stringify(post_data));
    post_req.end();
  }

  // Subscribe is called from anywhere in the hapi app like this
  // Params
  // - url: url of the feed/topic to which you want to subscribe
  // - id: unique id of the feed/topic uou want to subscribe. This is important to identify unique notifications. You should use some kind of primary key.
  // - opts:
  //      - retrieve: set to true if you want previous items from the feed
  //      - format: 'atom' or 'json'
  //      - secret: a unique secret used to compute an HMAC key for notifications.
  // - the callback will be called with the result of the subscription. First param is error, second param is body if retrieve:true.
  plugin.expose('subscribe', function(url, id, opts, cb) {
    _superfeedrRequest('subscribe', url, id, opts, cb);
  });

  // Unsubscribe is called from anywhere in the hapi app like this
  // Params
  // - url: url of the feed/topic to which you want to subscribe
  // - id: unique id of the feed/topic that you used to subscribe
  // - the callback will be called with the result of the subscription. First param is error, second param is body if retrieve:true.
  plugin.expose('unsubscribe', function(url, id, cb) {
    _superfeedrRequest('unsubscribe', url, id, {}, cb);
  });

  // Expose the events
  plugin.expose('events', plugin.events);

  plugin.route({
    method: 'POST',
    path: path.join(options.webhooks.base, '{feed_id?}'),
    handler: function(request, reply) {
      // The event is triggered so that anyone who listens to 'notification' on the superfeedr plugin (how do we do that?)
      // will recive the feed_id, the RAWBODY (if we are able to extract it), the topic/feed url and the request object
      // if they want to debug it.
      plugin.events.emit('notification', request.params.feed_id, request.payload, request.headers['x-pubsubhubbub-topic'], request);
      reply('Thanks Superfeedr');
    },
    config: {
      payload: {
        allow: ["application/atom+xml", "application/json"],
        parse: false
      }
    }
  });

  next();
}

exports.register.attributes = {
  multiple: false,
  name: 'superfeedr',
  version: '1.0.0'
};
<|endoftext|>


<commit_before>var chessground = require('chessground');
var opposite = chessground.util.opposite;
var data = require('./data');
var analyse = require('./analyse');
var ground = require('./ground');
var keyboard = require('./keyboard');
var treePath = require('./path');
var actionMenu = require('./actionMenu').controller;
var autoplay = require('./autoplay');
var control = require('./control');
var promotion = require('./promotion');
var readDests = require('./util').readDests;
var throttle = require('./util').throttle;
var socket = require('./socket');
var forecastCtrl = require('./forecast/forecastCtrl');
var cevalCtrl = require('./ceval/cevalCtrl');
var router = require('game').router;
var game = require('game').game;
var m = require('mithril');

module.exports = function(opts) {

  this.data = data({}, opts.data);
  this.analyse = new analyse(this.data.steps);
  this.actionMenu = new actionMenu();
  this.autoplay = new autoplay(this);

  this.userId = opts.userId;

  var initialPath = opts.path ? treePath.read(opts.path) : treePath.default(this.analyse.firstPly());
  if (initialPath[0].ply >= this.data.steps.length)
    initialPath = treePath.default(this.data.steps.length - 1);

  this.vm = {
    path: initialPath,
    pathStr: treePath.write(initialPath),
    initialPathStr: '' + opts.data.path,
    step: null,
    cgConfig: null,
    comments: true,
    flip: false
  };

  this.flip = function() {
    this.vm.flip = !this.vm.flip;
    this.chessground.set({
      orientation: this.vm.flip ? this.data.opponent.color : this.data.player.color
    });
  }.bind(this);

  this.togglePlay = function(delay) {
    this.autoplay.toggle(delay);
    this.actionMenu.open = false;
  }.bind(this);

  var showGround = function() {
    var s;
    try {
      s = this.analyse.getStep(this.vm.path);
    } catch (e) {
      console.log(e);
    }
    if (!s) {
      this.vm.path = treePath.default(this.analyse.firstPly());
      this.vm.pathStr = treePath.write(this.vm.path);
      s = this.analyse.getStep(this.vm.path);
    }
    var color = s.ply % 2 === 0 ? 'white' : 'black';
    var dests = readDests(s.dests);
    var config = {
      fen: s.fen,
      turnColor: color,
      movable: {
        color: dests && Object.keys(dests).length > 0 ? color : null,
        dests: dests || {}
      },
      check: s.check,
      lastMove: s.uci ? [s.uci.substr(0, 2), s.uci.substr(2, 2)] : null,
    };
    if (!dests && !s.check) {
      // premove while dests are loading from server
      // can't use when in check because it highlights the wrong king
      config.turnColor = opposite(color);
      config.movable.color = color;
    }
    this.vm.step = s;
    this.vm.cgConfig = config;
    if (!this.chessground)
      this.chessground = ground.make(this.data, config, userMove);
    this.chessground.set(config);
    if (opts.onChange) opts.onChange(config.fen, this.vm.path);
    if (!dests) getDests();
  }.bind(this);

  var getDests = throttle(200, false, function() {
    if (this.vm.step.dests) return;
    this.socket.sendAnaDests({
      variant: this.data.game.variant.key,
      fen: this.vm.step.fen,
      path: this.vm.pathStr
    });
  }.bind(this));

  var sound = {
    move: throttle(50, false, $.sound.move),
    capture: throttle(50, false, $.sound.capture),
    check: throttle(50, false, $.sound.check)
  };

  this.jump = function(path) {
    this.vm.path = path;
    this.vm.pathStr = treePath.write(path);
    if (window.history.replaceState)
      window.history.replaceState(null, null, '#' + path[0].ply);
    this.chessground.setAutoShapes([]);
    showGround();
    if (!this.vm.step.uci) sound.move(); // initial position
    else if (this.vm.justPlayed !== this.vm.step.uci) {
      if (this.vm.step.san.indexOf('x') !== -1) sound.capture();
      else sound.move();
      this.vm.justPlayed = null;
    }
    if (/\+|\#/.test(this.vm.step.san)) sound.check();
    startCeval();
  }.bind(this);

  this.userJump = function(path) {
    this.autoplay.stop();
    this.jump(path);
  }.bind(this);

  this.jumpToMain = function(ply) {
    this.userJump([{
      ply: ply,
      variation: null
    }]);
  }.bind(this);

  this.jumpToIndex = function(index) {
    this.jumpToMain(index + 1 + this.data.game.startedAtTurn);
  }.bind(this);

  var forsyth = function(role) {
    return role === 'knight' ? 'n' : role[0];
  };

  var userMove = function(orig, dest, capture) {
    this.vm.justPlayed = orig + dest;
    sound[capture ? 'capture' : 'move']();
    if (!promotion.start(this, orig, dest, sendMove)) sendMove(orig, dest);
  }.bind(this);

  var sendMove = function(orig, dest, prom) {
    var move = {
      orig: orig,
      dest: dest,
      variant: this.data.game.variant.key,
      fen: this.vm.step.fen,
      path: this.vm.pathStr
    };
    if (prom) move.promotion = prom;
    this.socket.sendAnaMove(move);
    // prepare premoving
    this.chessground.set({
      turnColor: this.chessground.data.movable.color,
      movable: {
        color: opposite(this.chessground.data.movable.color)
      }
    });
  }.bind(this);

  this.addStep = function(step, path) {
    var newPath = this.analyse.addStep(step, treePath.read(path));
    this.jump(newPath);
    m.redraw();
    this.chessground.playPremove();
  }.bind(this);

  this.addDests = function(dests, path) {
    this.analyse.addDests(dests, treePath.read(path));
    if (path === this.vm.pathStr) showGround();
    this.chessground.playPremove();
  }.bind(this);

  this.reset = function() {
    this.chessground.set(this.vm.situation);
    m.redraw();
  }.bind(this);

  this.encodeStepFen = function() {
    return this.vm.step.fen.replace(/\s/g, '_');
  }.bind(this);

  this.socket = new socket(opts.socketSend, this);

  this.forecast = opts.data.forecast ? forecastCtrl(
    opts.data.forecast,
    router.forecasts(this.data)) : null;

  this.ceval = cevalCtrl(this.data.game.id === 'synthetic' || !game.playable(this.data),
    throttle(100, false, function(res) {
      this.analyse.addClientEval(res.work.path, res.eval);
      this.chessground.setAutoShapes([{
        orig: res.eval.uci.slice(0, 2),
        dest: res.eval.uci.slice(2, 4),
        style: 4
      }]);
      m.redraw();
    }.bind(this)));

  var startCeval = function() {
    if (this.ceval.enabled())
      this.ceval.start(this.vm.path, this.analyse.getSteps(this.vm.path));
  }.bind(this);
  startCeval();

  this.toggleCeval = function() {
    this.chessground.setAutoShapes([]);
    this.ceval.toggle();
    startCeval();
  }.bind(this);

  this.trans = function(key) {
    var str = opts.i18n[key] || key;
    Array.prototype.slice.call(arguments, 1).forEach(function(arg) {
      str = str.replace('%s', arg);
    });
    return str;
  };

  showGround();
  keyboard(this);
};
<commit_msg>client eval only works with standard chess (test 960 later)<commit_after>var chessground = require('chessground');
var opposite = chessground.util.opposite;
var data = require('./data');
var analyse = require('./analyse');
var ground = require('./ground');
var keyboard = require('./keyboard');
var treePath = require('./path');
var actionMenu = require('./actionMenu').controller;
var autoplay = require('./autoplay');
var control = require('./control');
var promotion = require('./promotion');
var readDests = require('./util').readDests;
var throttle = require('./util').throttle;
var socket = require('./socket');
var forecastCtrl = require('./forecast/forecastCtrl');
var cevalCtrl = require('./ceval/cevalCtrl');
var router = require('game').router;
var game = require('game').game;
var m = require('mithril');

module.exports = function(opts) {

  this.data = data({}, opts.data);
  this.analyse = new analyse(this.data.steps);
  this.actionMenu = new actionMenu();
  this.autoplay = new autoplay(this);

  this.userId = opts.userId;

  var initialPath = opts.path ? treePath.read(opts.path) : treePath.default(this.analyse.firstPly());
  if (initialPath[0].ply >= this.data.steps.length)
    initialPath = treePath.default(this.data.steps.length - 1);

  this.vm = {
    path: initialPath,
    pathStr: treePath.write(initialPath),
    initialPathStr: '' + opts.data.path,
    step: null,
    cgConfig: null,
    comments: true,
    flip: false
  };

  this.flip = function() {
    this.vm.flip = !this.vm.flip;
    this.chessground.set({
      orientation: this.vm.flip ? this.data.opponent.color : this.data.player.color
    });
  }.bind(this);

  this.togglePlay = function(delay) {
    this.autoplay.toggle(delay);
    this.actionMenu.open = false;
  }.bind(this);

  var showGround = function() {
    var s;
    try {
      s = this.analyse.getStep(this.vm.path);
    } catch (e) {
      console.log(e);
    }
    if (!s) {
      this.vm.path = treePath.default(this.analyse.firstPly());
      this.vm.pathStr = treePath.write(this.vm.path);
      s = this.analyse.getStep(this.vm.path);
    }
    var color = s.ply % 2 === 0 ? 'white' : 'black';
    var dests = readDests(s.dests);
    var config = {
      fen: s.fen,
      turnColor: color,
      movable: {
        color: dests && Object.keys(dests).length > 0 ? color : null,
        dests: dests || {}
      },
      check: s.check,
      lastMove: s.uci ? [s.uci.substr(0, 2), s.uci.substr(2, 2)] : null,
    };
    if (!dests && !s.check) {
      // premove while dests are loading from server
      // can't use when in check because it highlights the wrong king
      config.turnColor = opposite(color);
      config.movable.color = color;
    }
    this.vm.step = s;
    this.vm.cgConfig = config;
    if (!this.chessground)
      this.chessground = ground.make(this.data, config, userMove);
    this.chessground.set(config);
    if (opts.onChange) opts.onChange(config.fen, this.vm.path);
    if (!dests) getDests();
  }.bind(this);

  var getDests = throttle(200, false, function() {
    if (this.vm.step.dests) return;
    this.socket.sendAnaDests({
      variant: this.data.game.variant.key,
      fen: this.vm.step.fen,
      path: this.vm.pathStr
    });
  }.bind(this));

  var sound = {
    move: throttle(50, false, $.sound.move),
    capture: throttle(50, false, $.sound.capture),
    check: throttle(50, false, $.sound.check)
  };

  this.jump = function(path) {
    this.vm.path = path;
    this.vm.pathStr = treePath.write(path);
    if (window.history.replaceState)
      window.history.replaceState(null, null, '#' + path[0].ply);
    this.chessground.setAutoShapes([]);
    showGround();
    if (!this.vm.step.uci) sound.move(); // initial position
    else if (this.vm.justPlayed !== this.vm.step.uci) {
      if (this.vm.step.san.indexOf('x') !== -1) sound.capture();
      else sound.move();
      this.vm.justPlayed = null;
    }
    if (/\+|\#/.test(this.vm.step.san)) sound.check();
    startCeval();
  }.bind(this);

  this.userJump = function(path) {
    this.autoplay.stop();
    this.jump(path);
  }.bind(this);

  this.jumpToMain = function(ply) {
    this.userJump([{
      ply: ply,
      variation: null
    }]);
  }.bind(this);

  this.jumpToIndex = function(index) {
    this.jumpToMain(index + 1 + this.data.game.startedAtTurn);
  }.bind(this);

  var forsyth = function(role) {
    return role === 'knight' ? 'n' : role[0];
  };

  var userMove = function(orig, dest, capture) {
    this.vm.justPlayed = orig + dest;
    sound[capture ? 'capture' : 'move']();
    if (!promotion.start(this, orig, dest, sendMove)) sendMove(orig, dest);
  }.bind(this);

  var sendMove = function(orig, dest, prom) {
    var move = {
      orig: orig,
      dest: dest,
      variant: this.data.game.variant.key,
      fen: this.vm.step.fen,
      path: this.vm.pathStr
    };
    if (prom) move.promotion = prom;
    this.socket.sendAnaMove(move);
    // prepare premoving
    this.chessground.set({
      turnColor: this.chessground.data.movable.color,
      movable: {
        color: opposite(this.chessground.data.movable.color)
      }
    });
  }.bind(this);

  this.addStep = function(step, path) {
    var newPath = this.analyse.addStep(step, treePath.read(path));
    this.jump(newPath);
    m.redraw();
    this.chessground.playPremove();
  }.bind(this);

  this.addDests = function(dests, path) {
    this.analyse.addDests(dests, treePath.read(path));
    if (path === this.vm.pathStr) showGround();
    this.chessground.playPremove();
  }.bind(this);

  this.reset = function() {
    this.chessground.set(this.vm.situation);
    m.redraw();
  }.bind(this);

  this.encodeStepFen = function() {
    return this.vm.step.fen.replace(/\s/g, '_');
  }.bind(this);

  this.socket = new socket(opts.socketSend, this);

  this.forecast = opts.data.forecast ? forecastCtrl(
    opts.data.forecast,
    router.forecasts(this.data)) : null;

  var allowCeval = (
    this.data.game.id === 'synthetic' || !game.playable(this.data)
  ) && this.data.game.variant.key === 'standard';
  this.ceval = cevalCtrl(allowCeval,
    throttle(100, false, function(res) {
      this.analyse.addClientEval(res.work.path, res.eval);
      this.chessground.setAutoShapes([{
        orig: res.eval.uci.slice(0, 2),
        dest: res.eval.uci.slice(2, 4),
        style: 4
      }]);
      m.redraw();
    }.bind(this)));

  var startCeval = function() {
    if (this.ceval.enabled())
      this.ceval.start(this.vm.path, this.analyse.getSteps(this.vm.path));
  }.bind(this);
  startCeval();

  this.toggleCeval = function() {
    this.chessground.setAutoShapes([]);
    this.ceval.toggle();
    startCeval();
  }.bind(this);

  this.trans = function(key) {
    var str = opts.i18n[key] || key;
    Array.prototype.slice.call(arguments, 1).forEach(function(arg) {
      str = str.replace('%s', arg);
    });
    return str;
  };

  showGround();
  keyboard(this);
};
<|endoftext|>