<commit_before>"use strict";

define(['jquery', 'rk4', 'concrete'], function($, rk4, Concrete) {

  const CONFIG = {
    VIEW_ID:        '#view',
    VIEW_WIDTH:     640,
    VIEW_HEIGHT:    480,
    VIEW_SCALE:     100,
    BTN_RESET_ID:   '.button-reset',
    BTN_START_ID:   '.button-start',
    DIV_START_ID:   '.button-start-div',
    BTN_STOP_ID:    '.button-stop',
    DIV_STOP_ID:    '.button-stop-div',
    BTN_CLEAR_ID:   '.button-clear',
    TIME_ID:        '.time',
    DISTANCE_ID:    '.distance',
    POS1_ID:        '.pos1',
    POS2_ID:        '.pos2',
    EKINETIC_ID:    '.ekinetic',
    EPOTENTIAL_ID:  '.epotential',
    ETOTAL_ID:      '.etotal',
    SLIDER_DT_ID:   '.slider-dt',
    DT_ID:          '.dt',
    VIEW_BOB_SIZE:  5,
    SIM_DT:         0.01
  };

  function App() {
    this.t = 0.0;
    this.dt = CONFIG.SIM_DT;
    this.tscale = 1.0;
    this.distance = 0.0;

    this.x0 = [
      3.14,    // theta1
      0.0,    // dtheta1
      3.14,    // theta2
      0.0     // dtheta2
    ];
    this.x = this.x0;

    this.position = undefined;
    this.previousPosition = undefined;
    this.energy = undefined;

    this.pendulum = {
      m1:   1.0,
      m2:   1.0,
      l1:   1.0,
      l2:   1.0,
      g:    9.81
    };

    this.solver            = new rk4.RK4();
    this.running           = false;
    this.interval          = undefined;

    this.viewContainer     = undefined;
    this.view              = undefined;
    this.pendulumLayer     = undefined;
    this.traceLayer        = undefined;
    this.gridLayer         = undefined;
  };

  var model = function(t, u, x, p) {
    var tau1 =  u[0];
    var tau2 =  u[1];
    var th1 =   x[0];
    var dth1 =  x[1];
    var th2 =   x[2];
    var dth2 =  x[3];

    var d2th1 = (-p.m2 * p.l2 * dth1 * dth1 * Math.sin(th1-th2) * Math.cos(th1-th2)  + p.g * p.m2 * Math.sin(th2) * Math.cos(th1-th2)
      - p.m2 * p.l2 * dth2 * dth2 * Math.sin(th1-th2) - (p.m1 + p.m2) * p.g * Math.sin(th1)) / (p.l1 * (p.m1 + p.m2) - p.m2 * p.l1 *
      Math.pow(Math.cos(th1-th2), 2));
    var d2th2 = (p.m2 * p.l2 * dth2 * dth2 * Math.sin(th1-th2) * Math.cos(th1-th2) + p.g * Math.sin(th1) * Math.cos(th1-th2) * (p.m1 + p.m2)
      + p.l1 * dth1 * dth1 * Math.sin(th1 - th2) * (p.m1 + p.m2) - p.g * Math.sin(th2) * (p.m1 + p.m2)) / (
      p.l2 * (p.m1 + p.m2) - p.m2 * p.l2 * Math.pow(Math.cos(th1-th2), 2));

    var dx = [
      x[1],
      d2th1,
      x[3],
      d2th2
    ];

    return dx;
  };

  function getMousePos(e, client) {
    var rect = client.getBoundingClientRect();
    return {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    };
  };

  App.prototype.initialize = function() {
    var self = this;

    this.viewContainer = $(CONFIG.VIEW_ID).get(0);
    this.view = new Concrete.Viewport({
      container: this.viewContainer,
      width: CONFIG.VIEW_WIDTH,
      height: CONFIG.VIEW_HEIGHT
    });
    this.pendulumLayer   = new Concrete.Layer();
    this.traceLayer      = new Concrete.Layer();
    this.gridLayer       = new Concrete.Layer();
    this.view            .add(this.gridLayer).add(this.traceLayer).add(this.pendulumLayer);

    $(CONFIG.BTN_RESET_ID).click(function() { self.reset(); });
    $(CONFIG.BTN_START_ID).click(function() { self.start(); });
    $(CONFIG.BTN_STOP_ID).click(function() { self.stop(); });
    $(CONFIG.BTN_CLEAR_ID).click(function() { self.clear(); });
    $(CONFIG.SLIDER_DT_ID).val(100).on('input change', function() {
        self.tscale = Math.pow(1.03271, 100-$(this).val());
        self.update();
    });

    $(CONFIG.VIEW_ID).click(function(e) { self.pullPendulum(e); });

    $(CONFIG.DIV_START_ID).show();
    $(CONFIG.DIV_STOP_ID).hide();

    this.position = this.calculatePosition(this.x, this.pendulum);
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    this.update();
  };

  App.prototype.drawPendulum = function(ctx, position) {
    var x1 = position.x1;
    var y1 = position.y1;
    var x2 = position.x2;
    var y2 = position.y2;

    ctx.save();
    ctx.fillStyle = 'black';
    ctx.strokeStyle = 'black';
    ctx.lineCap = 'round';
    ctx.lineWidth = 3;

    ctx.clearRect(0, 0, 800, 600);
    ctx.translate(CONFIG.VIEW_WIDTH/2, CONFIG.VIEW_HEIGHT/2);
    ctx.beginPath();
    ctx.arc(x1*CONFIG.VIEW_SCALE, y1*CONFIG.VIEW_SCALE, CONFIG.VIEW_BOB_SIZE, 0, 2*Math.PI);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x2*CONFIG.VIEW_SCALE, y2*CONFIG.VIEW_SCALE, CONFIG.VIEW_BOB_SIZE, 0, 2*Math.PI);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(x1*CONFIG.VIEW_SCALE, y1*CONFIG.VIEW_SCALE);
    ctx.lineTo(x2*CONFIG.VIEW_SCALE,y2*CONFIG.VIEW_SCALE);
    ctx.stroke();
    ctx.restore();
  };

  App.prototype.drawTrace = function(ctx, prevPos, pos) {
    if (!prevPos || !pos) return;

    ctx.save();
    ctx.lineCap = 'round';
    ctx.fillStyle = 'rgba(255, 255, 255, 0.01)';
    ctx.lineWidth = 2;

    ctx.fillRect(0, 0, CONFIG.VIEW_WIDTH, CONFIG.VIEW_HEIGHT);
    ctx.translate(CONFIG.VIEW_WIDTH/2, CONFIG.VIEW_HEIGHT/2);

    /*ctx.strokeStyle = 'blue';
    ctx.beginPath();
    ctx.moveTo(prevPos.x1*CONFIG.VIEW_SCALE, prevPos.y1*CONFIG.VIEW_SCALE);
    ctx.lineTo(pos.x1*CONFIG.VIEW_SCALE, pos.y1*CONFIG.VIEW_SCALE);
    ctx.closePath();
    ctx.stroke();*/

    ctx.strokeStyle = 'red';
    ctx.beginPath();
    ctx.moveTo(prevPos.x2*CONFIG.VIEW_SCALE, prevPos.y2*CONFIG.VIEW_SCALE);
    ctx.lineTo(pos.x2*CONFIG.VIEW_SCALE, pos.y2*CONFIG.VIEW_SCALE);
    ctx.closePath();
    ctx.stroke();
    ctx.restore();
  };

  App.prototype.jacobian = function(x, p) {
    return {
      dxdth1: p.l1 * Math.cos(x[0]),
      dxdth2: p.l2 * Math.cos(x[2]),
      dydth1: -p.l1 * Math.sin(x[0]),
      dydth2: -p.l2 * Math.sin(x[2])
    };
  };

  App.prototype.invKin = function(state, desiredPos, p, alpha, epsilon, maxIter) {
    var alpha = alpha || 0.1;
    var epsilon = epsilon || 1e-3;
    var maxIter = maxIter || 1000;
    var x = state.slice();

    var error = 0.0;
    var iter = 0;
    do {
      var position = this.calculatePosition(x, p);
      var dx = desiredPos.x - position.x2;
      var dy = desiredPos.y - position.y2;
      error = Math.sqrt(dx*dx + dy*dy);

      var J = this.jacobian(x, p);
      var dth1 = J.dxdth1 * dx + J.dydth1 * dy;
      var dth2 = J.dxdth2 * dx + J.dydth2 * dy;

      x[0] = x[0] + alpha * dth1;
      x[2] = x[2] + alpha * dth2;

      console.log(error);

      ++iter;
    } while (error > epsilon && iter < maxIter);

    x[1] = 0.0;
    x[3] = 0.0;

    return x;
  };

  App.prototype.pullPendulum = function(e) {
    var pos = getMousePos(e, $(CONFIG.VIEW_ID).get(0));
    pos.x = (pos.x - CONFIG.VIEW_WIDTH/2)  / CONFIG.VIEW_SCALE;
    pos.y = (pos.y - CONFIG.VIEW_HEIGHT/2)  / CONFIG.VIEW_SCALE;
    this.x = this.invKin(this.x, pos, this.pendulum);
    this.position = this.calculatePosition(this.x, this.pendulum);
    this.previousPosition = this.position;
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    this.update();
  };

  App.prototype.calculatePosition = function(x, p) {
    var x1 = p.l1 * Math.sin(x[0]);
    var y1 = p.l1 * Math.cos(x[0]);
    var x2 = x1 + p.l2 * Math.sin(x[2]);
    var y2 = y1 + p.l2 * Math.cos(x[2]);

    return {
      x1: x1,
      y1: y1,
      x2: x2,
      y2: y2
    };
  };

  App.prototype.calculateEnergy = function(x, p) {
    var Ek = 0.5 * (p.m1 + p.m2) * p.l1 * p.l1 * x[1] * x[1] + 0.5 * p.m2 * p.l2 * x[3] * x[3]
      + p.m2 * p.l1 * p.l2 * x[1] * x[3] * Math.cos(x[0] - x[2]);
    var Ep = - (p.m1 + p.m2) * p.g * p.l1 * Math.cos(x[0]) - p.m2 * p.g * p.l2 * Math.cos(x[2]);
    var E = Ek + Ep;

    return {
      Ek: Ek,
      Ep: Ep,
      E: E
    };
  };

  App.prototype.step = function() {
    var self = this;

    var dt = this.dt / this.tscale;
    this.t += dt;
    this.x = this.solver.solve(function(t, u, x) { return model(t, u, x, self.pendulum); }, this.t, [0, 0], this.x, dt);
    this.previousPosition = this.position;
    this.position = this.calculatePosition(this.x, this.pendulum);
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    var dpos = Math.sqrt(Math.pow(this.position.x2 - this.previousPosition.x2, 2) + Math.pow(this.position.y2 - this.previousPosition.y2, 2));
    this.distance += dpos;

    this.update();
  };

  App.prototype.update = function() {
    this.drawPendulum(this.pendulumLayer.scene.context, this.position);
    this.drawTrace(this.traceLayer.scene.context, this.previousPosition, this.position);

    $(CONFIG.TIME_ID).text(this.t.toFixed(2) + ' s');
    $(CONFIG.DISTANCE_ID).text(this.distance.toFixed(2) + ' m');
    $(CONFIG.POS1_ID).text(
      '(' + this.position.x1.toFixed(2) + ', '
      + this.position.y1.toFixed(2) + ') m'
    );
    $(CONFIG.POS2_ID).text(
      '(' + this.position.x2.toFixed(2) + ', '
      + this.position.y2.toFixed(2) + ') m'
    );
    $(CONFIG.EKINETIC_ID).text(this.energy.Ek.toFixed(2) + ' J');
    $(CONFIG.EPOTENTIAL_ID).text(this.energy.Ep.toFixed(2) + ' J');
    $(CONFIG.ETOTAL_ID).text(this.energy.E.toFixed(2) + ' J');
    $(CONFIG.DT_ID).text((1/this.tscale).toFixed(2) + 'x');
  };

  App.prototype.clear = function() {
    var ctx = this.traceLayer.scene.context;
    ctx.clearRect(0, 0, CONFIG.VIEW_WIDTH, CONFIG.VIEW_HEIGHT);
  };

  App.prototype.reset = function() {
    this.stop();
    this.clear();
    this.t = 0.0;
    this.distance = 0.0;
    this.x = this.x0;
    this.position = this.calculatePosition(this.x, this.pendulum);
    this.previousPosition = this.position;
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    this.update();
  };

  App.prototype.start = function() {
    var self = this;

    if (!this.running) {
      this.running = true;
      clearInterval(this.interval);
      this.interval = setInterval(function() { self.step(); }, 10);
    }

    $(CONFIG.DIV_START_ID).hide();
    $(CONFIG.DIV_STOP_ID).show();
  };

  App.prototype.stop = function() {
    this.running = false;
    clearInterval(this.interval);

    $(CONFIG.DIV_START_ID).show();
    $(CONFIG.DIV_STOP_ID).hide();
  };

  App.prototype.run = function() {
    this.initialize();
  };

  return {
    App: App
  };

});
<commit_msg>random color traces<commit_after>"use strict";

define(['jquery', 'rk4', 'concrete'], function($, rk4, Concrete) {

  const CONFIG = {
    VIEW_ID:        '#view',
    VIEW_WIDTH:     640,
    VIEW_HEIGHT:    480,
    VIEW_SCALE:     100,
    BTN_RESET_ID:   '.button-reset',
    BTN_START_ID:   '.button-start',
    DIV_START_ID:   '.button-start-div',
    BTN_STOP_ID:    '.button-stop',
    DIV_STOP_ID:    '.button-stop-div',
    BTN_CLEAR_ID:   '.button-clear',
    TIME_ID:        '.time',
    DISTANCE_ID:    '.distance',
    POS1_ID:        '.pos1',
    POS2_ID:        '.pos2',
    EKINETIC_ID:    '.ekinetic',
    EPOTENTIAL_ID:  '.epotential',
    ETOTAL_ID:      '.etotal',
    SLIDER_DT_ID:   '.slider-dt',
    DT_ID:          '.dt',
    VIEW_BOB_SIZE:  5,
    SIM_DT:         0.01
  };

  function App() {
    this.t = 0.0;
    this.dt = CONFIG.SIM_DT;
    this.tscale = 1.0;
    this.distance = 0.0;

    this.x0 = [
      3.1,    // theta1
      0.0,    // dtheta1
      3.14,    // theta2
      0.0     // dtheta2
    ];
    this.startx = this.x0;
    this.x = this.x0;

    this.position = undefined;
    this.previousPosition = undefined;
    this.energy = undefined;

    this.pendulum = {
      m1:   1.0,
      m2:   1.0,
      l1:   1.0,
      l2:   1.0,
      g:    9.81
    };

    this.solver            = new rk4.RK4();
    this.running           = false;
    this.interval          = undefined;

    this.viewContainer     = undefined;
    this.view              = undefined;
    this.pendulumLayer     = undefined;
    this.traceLayer        = undefined;
    this.gridLayer         = undefined;
    this.traceColor        = 'red';
  };

  var model = function(t, u, x, p) {
    var tau1 =  u[0];
    var tau2 =  u[1];
    var th1 =   x[0];
    var dth1 =  x[1];
    var th2 =   x[2];
    var dth2 =  x[3];

    var d2th1 = (-p.m2 * p.l2 * dth1 * dth1 * Math.sin(th1-th2) * Math.cos(th1-th2)  + p.g * p.m2 * Math.sin(th2) * Math.cos(th1-th2)
      - p.m2 * p.l2 * dth2 * dth2 * Math.sin(th1-th2) - (p.m1 + p.m2) * p.g * Math.sin(th1)) / (p.l1 * (p.m1 + p.m2) - p.m2 * p.l1 *
      Math.pow(Math.cos(th1-th2), 2));
    var d2th2 = (p.m2 * p.l2 * dth2 * dth2 * Math.sin(th1-th2) * Math.cos(th1-th2) + p.g * Math.sin(th1) * Math.cos(th1-th2) * (p.m1 + p.m2)
      + p.l1 * dth1 * dth1 * Math.sin(th1 - th2) * (p.m1 + p.m2) - p.g * Math.sin(th2) * (p.m1 + p.m2)) / (
      p.l2 * (p.m1 + p.m2) - p.m2 * p.l2 * Math.pow(Math.cos(th1-th2), 2));

    var dx = [
      x[1],
      d2th1,
      x[3],
      d2th2
    ];

    return dx;
  };

  var getMousePos = function(e, client) {
    var rect = client.getBoundingClientRect();
    return {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    };
  };

  var randomColor = function() {
    var r = Math.floor(256 * Math.random());
    var g = Math.floor(256 * Math.random());
    var b = Math.floor(256 * Math.random());

    var max = r > g ? (r > b ? r : b) : (g > b ? g : b);
    r *= 255 / max;
    g *= 255 / max;
    b *= 255 / max;

    return 'rgb(' + r.toFixed(0) + ', ' + g.toFixed(0) + ', ' + b.toFixed(0) + ')';
  };

  App.prototype.initialize = function() {
    var self = this;

    this.viewContainer = $(CONFIG.VIEW_ID).get(0);
    this.view = new Concrete.Viewport({
      container: this.viewContainer,
      width: CONFIG.VIEW_WIDTH,
      height: CONFIG.VIEW_HEIGHT
    });
    this.pendulumLayer   = new Concrete.Layer();
    this.traceLayer      = new Concrete.Layer();
    this.gridLayer       = new Concrete.Layer();
    this.view            .add(this.gridLayer).add(this.traceLayer).add(this.pendulumLayer);

    $(CONFIG.BTN_RESET_ID).click(function() { self.reset(); });
    $(CONFIG.BTN_START_ID).click(function() { self.start(); });
    $(CONFIG.BTN_STOP_ID).click(function() { self.stop(); });
    $(CONFIG.BTN_CLEAR_ID).click(function() { self.clear(); });
    $(CONFIG.SLIDER_DT_ID).val(100).on('input change', function() {
        self.tscale = Math.pow(1.03271, 100-$(this).val());
        self.update();
    });

    $(CONFIG.VIEW_ID).click(function(e) { self.pullPendulum(e); });

    $(CONFIG.DIV_START_ID).show();
    $(CONFIG.DIV_STOP_ID).hide();

    this.position = this.calculatePosition(this.x, this.pendulum);
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    this.update();
  };

  App.prototype.drawPendulum = function(ctx, position) {
    var x1 = position.x1;
    var y1 = position.y1;
    var x2 = position.x2;
    var y2 = position.y2;

    ctx.save();
    ctx.fillStyle = 'black';
    ctx.strokeStyle = 'black';
    ctx.lineCap = 'round';
    ctx.lineWidth = 3;

    ctx.clearRect(0, 0, 800, 600);
    ctx.translate(CONFIG.VIEW_WIDTH/2, CONFIG.VIEW_HEIGHT/2);
    ctx.beginPath();
    ctx.arc(x1*CONFIG.VIEW_SCALE, y1*CONFIG.VIEW_SCALE, CONFIG.VIEW_BOB_SIZE, 0, 2*Math.PI);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x2*CONFIG.VIEW_SCALE, y2*CONFIG.VIEW_SCALE, CONFIG.VIEW_BOB_SIZE, 0, 2*Math.PI);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(x1*CONFIG.VIEW_SCALE, y1*CONFIG.VIEW_SCALE);
    ctx.lineTo(x2*CONFIG.VIEW_SCALE,y2*CONFIG.VIEW_SCALE);
    ctx.stroke();
    ctx.restore();
  };

  App.prototype.drawTrace = function(ctx, prevPos, pos) {
    if (!prevPos || !pos) return;

    ctx.save();
    ctx.lineCap = 'round';
    ctx.fillStyle = 'rgba(255, 255, 255, 0.01)';
    ctx.lineWidth = 2;

    ctx.fillRect(0, 0, CONFIG.VIEW_WIDTH, CONFIG.VIEW_HEIGHT);
    ctx.translate(CONFIG.VIEW_WIDTH/2, CONFIG.VIEW_HEIGHT/2);

    /*ctx.strokeStyle = 'blue';
    ctx.beginPath();
    ctx.moveTo(prevPos.x1*CONFIG.VIEW_SCALE, prevPos.y1*CONFIG.VIEW_SCALE);
    ctx.lineTo(pos.x1*CONFIG.VIEW_SCALE, pos.y1*CONFIG.VIEW_SCALE);
    ctx.closePath();
    ctx.stroke();*/

    ctx.strokeStyle = this.traceColor;
    ctx.beginPath();
    ctx.moveTo(prevPos.x2*CONFIG.VIEW_SCALE, prevPos.y2*CONFIG.VIEW_SCALE);
    ctx.lineTo(pos.x2*CONFIG.VIEW_SCALE, pos.y2*CONFIG.VIEW_SCALE);
    ctx.closePath();
    ctx.stroke();
    ctx.restore();
  };

  App.prototype.jacobian = function(x, p) {
    return {
      dxdth1: p.l1 * Math.cos(x[0]),
      dxdth2: p.l2 * Math.cos(x[2]),
      dydth1: -p.l1 * Math.sin(x[0]),
      dydth2: -p.l2 * Math.sin(x[2])
    };
  };

  App.prototype.invKin = function(state, desiredPos, p, alpha, epsilon, maxIter) {
    var alpha = alpha || 0.1;
    var epsilon = epsilon || 1e-3;
    var maxIter = maxIter || 1000;
    var x = state.slice();

    var error = 0.0;
    var iter = 0;
    do {
      var position = this.calculatePosition(x, p);
      var dx = desiredPos.x - position.x2;
      var dy = desiredPos.y - position.y2;
      error = Math.sqrt(dx*dx + dy*dy);

      var J = this.jacobian(x, p);
      var dth1 = J.dxdth1 * dx + J.dydth1 * dy;
      var dth2 = J.dxdth2 * dx + J.dydth2 * dy;

      x[0] = x[0] + alpha * dth1;
      x[2] = x[2] + alpha * dth2;

      console.log(error);

      ++iter;
    } while (error > epsilon && iter < maxIter);

    x[1] = 0.0;
    x[3] = 0.0;

    return x;
  };

  App.prototype.pullPendulum = function(e) {
    var pos = getMousePos(e, $(CONFIG.VIEW_ID).get(0));
    pos.x = (pos.x - CONFIG.VIEW_WIDTH/2)  / CONFIG.VIEW_SCALE;
    pos.y = (pos.y - CONFIG.VIEW_HEIGHT/2)  / CONFIG.VIEW_SCALE;
    this.x = this.invKin(this.x, pos, this.pendulum);
    this.position = this.calculatePosition(this.x, this.pendulum);
    this.previousPosition = this.position;
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    this.traceColor = randomColor();
    this.update();
  };

  App.prototype.calculatePosition = function(x, p) {
    var x1 = p.l1 * Math.sin(x[0]);
    var y1 = p.l1 * Math.cos(x[0]);
    var x2 = x1 + p.l2 * Math.sin(x[2]);
    var y2 = y1 + p.l2 * Math.cos(x[2]);

    return {
      x1: x1,
      y1: y1,
      x2: x2,
      y2: y2
    };
  };

  App.prototype.calculateEnergy = function(x, p) {
    var Ek = 0.5 * (p.m1 + p.m2) * p.l1 * p.l1 * x[1] * x[1] + 0.5 * p.m2 * p.l2 * x[3] * x[3]
      + p.m2 * p.l1 * p.l2 * x[1] * x[3] * Math.cos(x[0] - x[2]);
    var Ep = - (p.m1 + p.m2) * p.g * p.l1 * Math.cos(x[0]) - p.m2 * p.g * p.l2 * Math.cos(x[2]);
    var E = Ek + Ep;

    return {
      Ek: Ek,
      Ep: Ep,
      E: E
    };
  };

  App.prototype.step = function() {
    var self = this;

    var dt = this.dt / this.tscale;
    this.t += dt;
    this.x = this.solver.solve(function(t, u, x) { return model(t, u, x, self.pendulum); }, this.t, [0, 0], this.x, dt);
    this.previousPosition = this.position;
    this.position = this.calculatePosition(this.x, this.pendulum);
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    var dpos = Math.sqrt(Math.pow(this.position.x2 - this.previousPosition.x2, 2) + Math.pow(this.position.y2 - this.previousPosition.y2, 2));
    this.distance += dpos;

    this.update();
  };

  App.prototype.update = function() {
    this.drawPendulum(this.pendulumLayer.scene.context, this.position);
    this.drawTrace(this.traceLayer.scene.context, this.previousPosition, this.position);

    $(CONFIG.TIME_ID).text(this.t.toFixed(2) + ' s');
    $(CONFIG.DISTANCE_ID).text(this.distance.toFixed(2) + ' m');
    $(CONFIG.POS1_ID).text(
      '(' + this.position.x1.toFixed(2) + ', '
      + this.position.y1.toFixed(2) + ') m'
    );
    $(CONFIG.POS2_ID).text(
      '(' + this.position.x2.toFixed(2) + ', '
      + this.position.y2.toFixed(2) + ') m'
    );
    $(CONFIG.EKINETIC_ID).text(this.energy.Ek.toFixed(2) + ' J');
    $(CONFIG.EPOTENTIAL_ID).text(this.energy.Ep.toFixed(2) + ' J');
    $(CONFIG.ETOTAL_ID).text(this.energy.E.toFixed(2) + ' J');
    $(CONFIG.DT_ID).text((1/this.tscale).toFixed(2) + 'x');
  };

  App.prototype.clear = function() {
    var ctx = this.traceLayer.scene.context;
    ctx.clearRect(0, 0, CONFIG.VIEW_WIDTH, CONFIG.VIEW_HEIGHT);
  };

  App.prototype.reset = function() {
    this.stop();
    this.clear();
    this.t = 0.0;
    this.distance = 0.0;
    this.x = this.x0;
    this.position = this.calculatePosition(this.x, this.pendulum);
    this.previousPosition = this.position;
    this.energy = this.calculateEnergy(this.x, this.pendulum);
    this.update();
  };

  App.prototype.start = function() {
    var self = this;

    if (!this.running) {
      this.running = true;
      clearInterval(this.interval);
      this.interval = setInterval(function() { self.step(); }, 10);
    }

    $(CONFIG.DIV_START_ID).hide();
    $(CONFIG.DIV_STOP_ID).show();
  };

  App.prototype.stop = function() {
    this.running = false;
    clearInterval(this.interval);

    $(CONFIG.DIV_START_ID).show();
    $(CONFIG.DIV_STOP_ID).hide();
  };

  App.prototype.run = function() {
    this.initialize();
  };

  return {
    App: App
  };

});
<|endoftext|>


<commit_before>var socketio = require('socket.io');
var utils = require('./utils.js');
var _ = require('lodash');

/**
 * Handle all of our socket connections
 */
module.exports = function (server) {
  io = socketio(server);

  // Change heartbeat to 40s - within Heroku's 55s limit
  io.set('heartbeat interval', 40000);

  var viewer = io.of('/view');

  viewer.on('connection', function (socket) {
    socket.emit('welcome', { id: utils.getPublicId() });
  });

  var touch = io.of('/touch');

  touch.on('connection', function (socket) {
    socket.touchPointers = [];
    socket.on('pointers', function (data) {
      socket.touchPointers = data;
    });
  });

  var emitPointers = function () {
    var pointers = [];
    _.forOwn(touch.connected, function (socket) {
      socket.touchPointers.forEach(function(pointer) {
        if (pointer) {
          // Add ID to pointer
          pointer.push(socket.id);
          pointers.push(pointer);
        }
      });
    });

    viewer.emit('pointers', pointers);
  };

  setInterval(emitPointers, 50);
};
<commit_msg>Automatically kill old touches<commit_after>var socketio = require('socket.io');
var utils = require('./utils.js');
var moment = require('moment');
var _ = require('lodash');

/**
 * Handle all of our socket connections
 */
module.exports = function (server) {
  io = socketio(server);

  // Change heartbeat to 40s - within Heroku's 55s limit
  io.set('heartbeat interval', 40000);

  var viewer = io.of('/view');

  viewer.on('connection', function (socket) {
    socket.emit('welcome', { id: utils.getPublicId() });
  });

  var touch = io.of('/touch');

  touch.on('connection', function (socket) {
    socket.touchPointers = [];
    socket.on('pointers', function (data) {
      socket.touchPointers = data;
      socket.age = moment();
    });
  });

  var emitPointers = function () {
    var pointers = [];
    _.forOwn(touch.connected, function (socket) {
      socket.touchPointers.forEach(function(pointer) {
        if (pointer) {
          // Add ID to pointer
          pointer.push(socket.id);
          pointers.push(pointer);
        }
      });
    });

    viewer.emit('pointers', pointers);
  };

  var clearPointers = function () {
    _.forOwn(touch.connected, function (socket) {
      if ((socket.age) && (moment().diff(socket.age) > 1000)) {
        socket.touchPointers = [];
      }
    });
  }

  setInterval(emitPointers, 50);
  setInterval(clearPointers, 1000);
};
<|endoftext|>


<commit_before>var config = require('./config/config_server');
var logger = require('./logger');

var moment = require('moment');
var monk = require('monk');
var cookieParser = require('cookie-parser');
var passportio = require('passport.socketio');

var db = monk(config.mongoURL);
var chats = db.get('history');
var Item = require('./models/items');
var User = require('./models/users');
var Character = require('./models/character');

var io;
var rooms = {}; // { room_name: client_count }
var buddyList = [];

var init = function(sio, passport, sessionStore) {
  io = sio;
  sessions = sessionStore;
  io.sockets.on('connection', onConnect);
  io.set('authorization', passportio.authorize({
    cookieParser : cookieParser,
    key: config.sessionKey,
    secret: config.sessionSecret,
    store: sessionStore,
    success: authSuccess,
    failure: authFailure
  }));
}

var closeSocketForUser = function(user) {
  var socket = io.sockets.adapter.rooms[user._id][0]; // currently only one socket per user. Works for now, fix this later
  socket.disconnect();
}

var updateInventoryForCharacter = function(charId) {
  Item.find({ owned_by: charId }, function (err, doc) {
    io.to(charId).emit('update-inventory', { inventory: doc });
  });
}

var onConnect = function (socket) {
  var user = getUserFromSocket(socket);

  socket.on('subscribe', function(data) { onSubscribe(socket, data.room); });
  socket.on('unsubscribe', function(data) { onUnsubscribe(socket, data.room); });
  socket.on('disconnect', function() { onDisconnect(socket) });

  socket.on('chat-send', function(data) { onChatReceived(socket, data); });
  socket.on('item-transfer', function(data) { onItemTransfer(data); });

  // auto-join some useful rooms
  socket.join(user._id);
  socket.join(user.currentChar);

  if (user.getPermissionLevel() == 0)
    socket.join('admins');
  if (user.getPermissionLevel() == 1)
    socket.join('superusers');
  if (user.getPermissionLevel() == 2)
    socket.join('players');
}

var onDisconnect = function(socket) {
  logger.info('Socket disconnected. ID: ' + socket.id);
  var roomNames = Object.keys(rooms);
  for (var i in roomNames) {
    onUnsubscribe(socket, roomNames[i]);
  }
}

var onSubscribe = function(socket, room) {
  var user = getUserFromSocket(socket);
  socket.join(room);
  if (rooms[room] == undefined) {
    rooms[room] = 1;
  } else {
    rooms[room] += 1;
  }

  // custom startup behavior for different rooms
  if (room == 'chat') {
    // Login message (to the user)
    socket.emit('message', {
      message: 'Connected to chat. Displaying ' + config.displayRecent + ' most recent messages...',
      timestap: moment(),
      recipients: [user.username]
    });
    sendRecentHistory(socket);

    // joined notification (to everyone)
    socket.broadcast.to(room).emit('message', {
      message: 'User ' + user.username + ' has joined the chat.',
      timestamp: moment(),
      recipients: 'all'
    });

    addToBuddyList(socket);
  }
  if (room == 'inventory') {
    sendInventory(socket);
  }
}

var onUnsubscribe = function(socket, room) {
  var user = getUserFromSocket(socket);
  socket.leave(room);
  rooms[room] -= 1;

  // custom exit behavior from different rooms
  if (room == 'chat') {
    // exited notification (to everyone)
    io.to(room).emit('message', {
      message: 'User ' + user.username + ' has left the chat.',
      timestamp: moment(),
      recipients: 'all'
    });

    removeFromBuddyList(user);
  }
}

var onChatReceived = function(socket, data) {
  var targetSockets = [];
  var room = 'chat';
  var user = getUserFromSocket(socket);
  data.username = user.username;
  if (data.recipients == undefined) {
    logger.debug("Initializing chat-send listener: recipients is undefined");
    data.recipients = 'all';
  } else if (data.recipients.length == 0) {
    logger.debug("Initializing chat-send listener: recipients is empty");
    data.recipients = 'all';
  }

  if (data.recipients == 'all') {
    io.to(room).emit('message', data);
  } else {
    User.find({ _id: { $in: data.recipients } }, function(err, doc) {
      data.recipients = [];
      for (var i = 0; i < doc.length; i++) {
        data.recipients.push(doc[i].username);
        io.to(doc[i]._id).emit('message', data);
      }
      io.to(user._id).emit('message', data);
      chats.insert(data);
    });
  }
}

var onItemTransfer = function(data) {
  Item.find({_id: {$in: data.items} }, function(err, items) {
    if (items.length > 0) {
      var currentOwner = items[0].owned_by;
      var targetChar = data.recipient;
      for (var i = 0; i < items.length; i++) {
        items[i].giveToCharacter(data.recipient, function(item) {
          updateInventoryForCharacter(currentOwner);
          updateInventoryForCharacter(targetChar);
        });
      }
    }
  });
}

var sendRecentHistory = function (socket) {
  var user = getUserFromSocket(socket);
  chats.find(
    { $or: [{recipients: 'all'}, {recipients: user.username}, {username: user.username}] },
    { limit: config.displayRecent, sort: {'timestamp': -1} },
    function (err, doc) {
      // work backwards to send recent history in chronological order
      for (var i = 1; i <= doc.length; i++) {
        socket.emit('message', doc[doc.length-i]);
      }
    }
  );
}

var addToBuddyList = function(socket) {
  var user = getUserFromSocket(socket);
  user.populate('currentChar', function(err, fullUser) {
    if (err) throw err;
    if (fullUser) {
      buddyList.push(user);
    }
    io.to('chat').emit('active-users', { users: buddyList });
  });
}

var removeFromBuddyList = function(user) {
  for (var i = 0; i < buddyList.length; i++) {
    if (buddyList[i]._id == user._id) {
      buddyList.splice(i, 1);
      i--;
    }
  }
  io.to('chat').emit('active-users', { users: buddyList });
}

var getUserFromSocket = function(socket) {
  var user = socket.client.request.user;
  return user;
}

var authSuccess = function(data, accept) {
  logger.info("Socket initiated by user " + data.user.username);
  accept(null, true);
}

var authFailure = function(data, message, error, accept) {
  if (error) throw new Error(message);
  logger.info("Socket auth failed: " + message);
  accept(null, false);
}

// hacking in inventory stuff, this shit will need to be refactored
var sendInventory = function(socket) {
  var user = getUserFromSocket(socket);
  updateInventoryForCharacter(user.currentChar);
}

////////////////////////////////////////////////////////
// Public Interface
module.exports.init = init;
module.exports.closeSocketForUser = closeSocketForUser;
module.exports.updateInventoryForCharacter = updateInventoryForCharacter;
<commit_msg>Fix #58<commit_after>var config = require('./config/config_server');
var logger = require('./logger');

var moment = require('moment');
var monk = require('monk');
var cookieParser = require('cookie-parser');
var passportio = require('passport.socketio');

var db = monk(config.mongoURL);
var chats = db.get('history');
var Item = require('./models/items');
var User = require('./models/users');
var Character = require('./models/character');

var io;
var rooms = {}; // { room_name: client_count }
var buddyList = [];

var init = function(sio, passport, sessionStore) {
  io = sio;
  sessions = sessionStore;
  io.sockets.on('connection', onConnect);
  io.set('authorization', passportio.authorize({
    cookieParser : cookieParser,
    key: config.sessionKey,
    secret: config.sessionSecret,
    store: sessionStore,
    success: authSuccess,
    failure: authFailure
  }));
}

var closeSocketForUser = function(user) {
  var socket = io.sockets.adapter.rooms[user._id][0]; // currently only one socket per user. Works for now, fix this later
  socket.disconnect();
}

var updateInventoryForCharacter = function(charId) {
  Item.find({ owned_by: charId }, function (err, doc) {
    io.to(charId).emit('update-inventory', { inventory: doc });
  });
}

var onConnect = function (socket) {
  var user = getUserFromSocket(socket);

  socket.on('subscribe', function(data) { onSubscribe(socket, data.room); });
  socket.on('unsubscribe', function(data) { onUnsubscribe(socket, data.room); });
  socket.on('disconnect', function() { onDisconnect(socket) });

  socket.on('chat-send', function(data) { onChatReceived(socket, data); });
  socket.on('item-transfer', function(data) { onItemTransfer(data); });

  // auto-join some useful rooms
  socket.join(user._id);
  socket.join(user.currentChar);

  if (user.getPermissionLevel() == 0)
    socket.join('admins');
  if (user.getPermissionLevel() == 1)
    socket.join('superusers');
  if (user.getPermissionLevel() == 2)
    socket.join('players');
}

var onDisconnect = function(socket) {
  logger.info('Socket disconnected. ID: ' + socket.id);
  var roomNames = Object.keys(rooms);
  for (var i in roomNames) {
    onUnsubscribe(socket, roomNames[i]);
  }
}

var onSubscribe = function(socket, room) {
  var user = getUserFromSocket(socket);
  socket.join(room);
  if (rooms[room] == undefined) {
    rooms[room] = 1;
  } else {
    rooms[room] += 1;
  }

  // custom startup behavior for different rooms
  if (room == 'chat') {
    // Login message (to the user)
    socket.emit('message', {
      message: 'Connected to chat. Displaying ' + config.displayRecent + ' most recent messages...',
      timestap: moment(),
      recipients: [user.username]
    });
    sendRecentHistory(socket);

    // joined notification (to everyone)
    socket.broadcast.to(room).emit('message', {
      message: 'User ' + user.username + ' has joined the chat.',
      timestamp: moment(),
      recipients: 'all'
    });

    addToBuddyList(socket);
  }
  if (room == 'inventory') {
    sendInventory(socket);
  }
}

var onUnsubscribe = function(socket, room) {
  var user = getUserFromSocket(socket);
  socket.leave(room);
  rooms[room] -= 1;

  // custom exit behavior from different rooms
  if (room == 'chat') {
    // exited notification (to everyone)
    io.to(room).emit('message', {
      message: 'User ' + user.username + ' has left the chat.',
      timestamp: moment(),
      recipients: 'all'
    });

    removeFromBuddyList(user);
  }
}

var onChatReceived = function(socket, data) {
  var targetSockets = [];
  var room = 'chat';
  var user = getUserFromSocket(socket);
  data.username = user.username;
  if (data.recipients == undefined) {
    logger.debug("Initializing chat-send listener: recipients is undefined");
    data.recipients = 'all';
  } else if (data.recipients.length == 0) {
    logger.debug("Initializing chat-send listener: recipients is empty");
    data.recipients = 'all';
  }

  if (data.recipients == 'all') {
    io.to(room).emit('message', data);
  } else {
    User.find({ _id: { $in: data.recipients } }, function(err, doc) {
      data.recipients = [];
      for (var i = 0; i < doc.length; i++) {
        data.recipients.push(doc[i].username);
        io.to(doc[i]._id).emit('message', data);
      }
      io.to(user._id).emit('message', data);
      chats.insert(data);
    });
  }
}

var onItemTransfer = function(data) {
  Item.find({_id: {$in: data.items} }, function(err, items) {
    if (items.length > 0) {
      var currentOwner = items[0].owned_by;
      var targetChar = data.recipient;
      for (var i = 0; i < items.length; i++) {
        items[i].giveToCharacter(data.recipient, function(item) {
          updateInventoryForCharacter(currentOwner);
          updateInventoryForCharacter(targetChar);
        });
      }
    }
  });
}

var sendRecentHistory = function (socket) {
  var user = getUserFromSocket(socket);
  chats.find(
    { $or: [{recipients: 'all'}, {recipients: user.username}, {username: user.username}] },
    { limit: config.displayRecent, sort: {'timestamp': -1} },
    function (err, doc) {
      // work backwards to send recent history in chronological order
      for (var i = 1; i <= doc.length; i++) {
        socket.emit('message', doc[doc.length-i]);
      }
    }
  );
}

var addToBuddyList = function(socket) {
  var user = getUserFromSocket(socket);
  user.populate('currentChar', function(err, fullUser) {
    if (err) throw err;
    if (fullUser) {
      buddyList.push(user);
    }
    io.to('chat').emit('active-users', { users: buddyList });
  });
}

var removeFromBuddyList = function(user) {
  for (var i = 0; i < buddyList.length; i++) {
    if (buddyList[i]._id == user._id) {
      buddyList.splice(i, 1);
      i--;
    }
  }
  io.to('chat').emit('active-users', { users: buddyList });
}

var getUserFromSocket = function(socket) {
  var user = socket.client.request.user;
  return user;
}

var authSuccess = function(data, accept) {
  logger.info("Socket initiated by user " + data.user.username);
  accept(null, true);
}

var authFailure = function(data, message, error, accept) {
  if (error) throw new Error(message);
  logger.info("Socket auth failed: " + message);
  accept(null, false);
}

// hacking in inventory stuff, this shit will need to be refactored
var sendInventory = function(socket) {
  var user = getUserFromSocket(socket);
  updateInventoryForCharacter(user.currentChar.id);
}

////////////////////////////////////////////////////////
// Public Interface
module.exports.init = init;
module.exports.closeSocketForUser = closeSocketForUser;
module.exports.updateInventoryForCharacter = updateInventoryForCharacter;
<|endoftext|>


<commit_before>/*jshint node: true */
'use strict';

var WebDriver = require('selenium-webdriver'),
	SeleniumServer = require('selenium-webdriver/remote').SeleniumServer,
	path = require('path'),
	ksInject = require('../inject'),
	ks = path.resolve(__dirname, '../../../../../kensington.js'),
	jar = path.resolve(__dirname, '../../build/selenium-server-standalone-2.41.0.jar');

module.exports = function (grunt) {
	var ksSource = grunt.file.read(ks);
	grunt.registerMultiTask('ks-selenium', function () {

		var done = this.async(),
			count = this.data.length;

		// start the selenium server
		var server = new SeleniumServer(jar, {
			port: 4444,
			args: ['-Xmx512m']
		});

		server.start();

		var driver = new WebDriver.Builder()
			.usingServer(server.address())
			.withCapabilities(WebDriver.Capabilities.firefox())
			.build();

		driver.manage().timeouts().setScriptTimeout(10000);

		this.data.forEach(function (testUrl) {
			driver.get(testUrl)
				.then(function () {
					ksInject(ksSource, driver, function () {
						driver.executeAsyncScript(function () {
							/*global document, dqre */
							var callback = arguments[arguments.length - 1];
							dqre.a11yCheck(document, null, function (results) {
								callback({results: results, url: window.location.href});
							});
						})
						.then(function (result) {
							grunt.file.write(result.url.replace(/[^a-z0-9]/gi, '-')
								.replace(/-{2,}/g, '-').replace(/^-|-$/g, '').toLowerCase() + '.json',
								JSON.stringify(result.results, null, '  '));

							if (!--count) {
								driver.quit();
								done();
							}
						});

					});
				});
		});

	});
};<commit_msg>Sneaky lint fix<commit_after>/*jshint node: true */
'use strict';

var WebDriver = require('selenium-webdriver'),
	SeleniumServer = require('selenium-webdriver/remote').SeleniumServer,
	path = require('path'),
	ksInject = require('../inject'),
	ks = path.resolve(__dirname, '../../../../../kensington.js'),
	jar = path.resolve(__dirname, '../../build/selenium-server-standalone-2.41.0.jar');

module.exports = function (grunt) {
	var ksSource = grunt.file.read(ks);
	grunt.registerMultiTask('ks-selenium', function () {

		var done = this.async(),
			count = this.data.length;

		// start the selenium server
		var server = new SeleniumServer(jar, {
			port: 4444,
			args: ['-Xmx512m']
		});

		server.start();

		var driver = new WebDriver.Builder()
			.usingServer(server.address())
			.withCapabilities(WebDriver.Capabilities.firefox())
			.build();

		driver.manage().timeouts().setScriptTimeout(10000);

		this.data.forEach(function (testUrl) {
			driver.get(testUrl)
				.then(function () {
					ksInject(ksSource, driver, function () {
						driver.executeAsyncScript(function () {
							/*global document, dqre, window */
							var callback = arguments[arguments.length - 1];
							dqre.a11yCheck(document, null, function (results) {
								callback({results: results, url: window.location.href});
							});
						})
						.then(function (result) {
							grunt.file.write(result.url.replace(/[^a-z0-9]/gi, '-')
								.replace(/-{2,}/g, '-').replace(/^-|-$/g, '').toLowerCase() + '.json',
								JSON.stringify(result.results, null, '  '));

							if (!--count) {
								driver.quit();
								done();
							}
						});

					});
				});
		});

	});
};<|endoftext|>


<commit_before>
var readArray = exports.readArray = function (array) {
  var i = 0
  return function (end, cb) {
    if(end)
      return cb && cb(end)

    cb(i >= array.length || null, array[i++])
  }
}

var count = function () {
  var i = 0
  return function (end, cb) {
    if(end) return cb && cb(end)
    cb(null, i++)
  }
}

var infinite = exports.infinite =
function (generate) {
  generate = generate || Math.random
  return function (end, cb) {
    if(end) return cb && cb(end)
    return cb(null, generate())
  }
}
var defer = exports.defer = function () {
  var _read, _cb, _end

  var read = function (end, cb) {
    if(_cb && !_read) throw new Error(
      'do not read twice' +
      'without waiting for callback'
    )
    if(_read) return _read(end, cb)
    _end = end, _cb = cb
  }
  read.resolve = function (read) {
    if(_read) throw new Error('already resolved')
    _read = read
    if(_cb) _read(_end, _cb)
  }
  read.abort = function(err) {
    read.resolve(function (_, cb) {
      cb(err || true)
    })
  }
  return read
}

var PushBuffer = exports.PushBuffer = function () {
  var buffer = [], cbs = [], waiting = [], ended

  function drain() {
    while(waiting.length && (buffer.length || ended)) {
      var data = buffer.shift()
      var cb   = cbs.shift()

      waiting.shift()(ended, data)
      cb && cb(ended)
    }
  }

  function read (end, cb) {
    ended = ended || end
    waiting.push(cb)
    drain()
  }

  read.push = function (data, cb) {
    buffer.push(data); cbs.push(cb)
    drain()
  }

  read.end = function (end, cb) {
    if('function' === typeof end)
      cb = end, end = true
    ended = ended || end || true; cbs.push(cb)
    drain()
  }

  return read

}

var depthFirst = exports.depthFirst =
function (start, createStream) {
  var reads = []

  reads.unshift(createStream(start))

  return function next (end, cb) {
    if(!reads.length)
      return cb(true)
    reads[0](end, function (end, data) {
      if(end) {
        //if this stream has ended, go to the next queue
        reads.shift()
        return next(null, cb)
      }
      reads.unshift(createStream(data))
      cb(end, data)
    })
  }
}
//width first is just like depth first,
//but push each new stream onto the end of the queue
var widthFirst = exports.widthFirst =
function (start, createStream) {
  var reads = []

  reads.push(createStream(start))

  return function next (end, cb) {
    if(!reads.length)
      return cb(true)
    reads[0](end, function (end, data) {
      if(end) {
        reads.shift()
        return next(null, cb)
      }
      reads.push(createStream(data))
      cb(end, data)
    })
  }
}

var leafFirst = exports.leafFirst =
function (start, createStream) {
  var reads = []
  var output = []
  reads.push(createStream(start))

  return function next (end, cb) {
    reads[0](end, function (end, data) {
      if(end) {
        reads.shift()
        if(!output.length)
          return cb(true)
        return cb(null, output.shift())
      }
      reads.unshift(createStream(data))
      output.unshift(data)
      next(null, cb)
    })
  }
}

<commit_msg>PushBuffer -> pushable<commit_after>
var readArray = exports.readArray = function (array) {
  var i = 0
  return function (end, cb) {
    if(end)
      return cb && cb(end)

    cb(i >= array.length || null, array[i++])
  }
}

var count = function (max) {
  var i = 0; max = max || Infinity
  return function (end, cb) {
    if(end) return cb && cb(end)
    if(max <= i)
      return cb(true)
    cb(null, i++)
  }
}

var infinite = exports.infinite =
function (generate) {
  generate = generate || Math.random
  return function (end, cb) {
    if(end) return cb && cb(end)
    return cb(null, generate())
  }
}
var defer = exports.defer = function () {
  var _read, _cb, _end

  var read = function (end, cb) {
    if(_cb && !_read) throw new Error(
      'do not read twice' +
      'without waiting for callback'
    )
    if(_read) return _read(end, cb)
    _end = end, _cb = cb
  }
  read.resolve = function (read) {
    if(_read) throw new Error('already resolved')
    _read = read
    if(_cb) _read(_end, _cb)
  }
  read.abort = function(err) {
    read.resolve(function (_, cb) {
      cb(err || true)
    })
  }
  return read
}
var pushable = exports.pushable = function () {
  var buffer = [], cbs = [], waiting = [], ended

  function drain() {
    while(waiting.length && (buffer.length || ended)) {
      var data = buffer.shift()
      var cb   = cbs.shift()

      waiting.shift()(ended, data)
      cb && cb(ended)
    }
  }

  function read (end, cb) {
    ended = ended || end
    waiting.push(cb)
    drain()
  }

  read.push = function (data, cb) {
    buffer.push(data); cbs.push(cb)
    drain()
  }

  read.end = function (end, cb) {
    if('function' === typeof end)
      cb = end, end = true
    ended = ended || end || true; cbs.push(cb)
    drain()
  }

  return read

}

var depthFirst = exports.depthFirst =
function (start, createStream) {
  var reads = []

  reads.unshift(createStream(start))

  return function next (end, cb) {
    if(!reads.length)
      return cb(true)
    reads[0](end, function (end, data) {
      if(end) {
        //if this stream has ended, go to the next queue
        reads.shift()
        return next(null, cb)
      }
      reads.unshift(createStream(data))
      cb(end, data)
    })
  }
}
//width first is just like depth first,
//but push each new stream onto the end of the queue
var widthFirst = exports.widthFirst =
function (start, createStream) {
  var reads = []

  reads.push(createStream(start))

  return function next (end, cb) {
    if(!reads.length)
      return cb(true)
    reads[0](end, function (end, data) {
      if(end) {
        reads.shift()
        return next(null, cb)
      }
      reads.push(createStream(data))
      cb(end, data)
    })
  }
}

//this came out different to the first (strm)
//attempt at leafFirst, but it's still a valid
//topological sort.
var leafFirst = exports.leafFirst =
function (start, createStream) {
  var reads = []
  var output = []
  reads.push(createStream(start))

  return function next (end, cb) {
    reads[0](end, function (end, data) {
      if(end) {
        reads.shift()
        if(!output.length)
          return cb(true)
        return cb(null, output.shift())
      }
      reads.unshift(createStream(data))
      output.unshift(data)
      next(null, cb)
    })
  }
}

<|endoftext|>


<commit_before>'use strict';
var Promise = require('bluebird');

var inquirer = require('inquirer');
var Botkit = require('botkit');
var serialPort = require('serialport');
var SerialPort = serialPort.SerialPort;
var http = require('http');
var https = require('https');
var fs = require('fs');
var exec = require('child_process').exec;

Promise.promisifyAll(inquirer);
Promise.promisifyAll(Botkit);
Promise.promisifyAll(serialPort);
Promise.promisifyAll(SerialPort);
Promise.promisifyAll(http);
Promise.promisifyAll(https);
Promise.promisifyAll(fs);
Promise.promisifyAll(exec);

serialPort.listAsync().then(ports => ports)
    .then(ports => {
    return new Promise(resolve => {
        inquirer.prompt(getQuestions(ports), answers => {
            resolve(answers);
        });
    });
})

    .then(answers => {
    var data = {
        answers: answers
    };

    if (data.answers.slackToken) {
        new Promise(resolve => {

            console.log('\n\nYour sparkedbot is being created!\nType "dosparked help" in slack to get a list of commands in slack\n\n');

            var Botkit = require('botkit');

            data.sparkedbot = Botkit.slackbot({
                debug: false,
                log: true
            });

            data.connect = function() {
                data.sparkedbot.spawn({
                    token: data.answers.slackToken,
                }).startRTM(err => {
                    if (err) {
                        if (!data.reconnects) {
                            data.reconnects = 1;
                        } else if (data.reconnects < 3) {

                        }
                        console.log('Could not connect to slack, retrying (' + data.reconnects + ')');
                    }
                });
            }

            data.connect();

            resolve(data);
        });
    }
    data.serialPort = new SerialPort(data.answers.port, {
        baudrate: data.answers.baud,
        parser: serialPort.parsers.readline('\n')
    }, true);
    data.serialPort.on('open', () => {
        data.serialOpen = true;
    });
    return data;
})

    .then(data => {
    if (data.sparkedbot) {

        data.sparkedbot.hears(['help'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'Hello.');
        });

        data.sparkedbot.hears(['example'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'Hello.');
        });

        data.sparkedbot.hears(['status', 'current'], ['direct_message', 'direct_mention'], (bot, message) => {
            var attachments = [];
            var attachment = {
                title: 'Status:',
                color: '#FDD835',
                fields: [],
            }

            attachment.fields.push({
                label: 'Field',
                value: 'Current port: ' + data.answers.port,
                short: false,
            })

            attachment.fields.push({
                label: 'Field',
                value: 'Current baud rate: ' + data.answers.baud,
                short: false,
            })

            attachment.fields.push({
                label: 'Field',
                value: 'File being monitored: ' + data.answers.filepath,
                short: false,
            })

            attachments.push(attachment);

            bot.reply(message, {
                text: '',
                attachments: attachments,
            }, (err,resp) => {
                //do nothing for now
            });
        });

        data.sparkedbot.hears(['refetch', 'reupload', 'upload', 'update'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'Refetching and uploading "' + data.answers.filepath + '"');
            data.forcedUpdate = true;
            upload(data);
        });

        data.sparkedbot.hears(['baud (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            var baud = message.match[1];
            var baudrates = getQuestions()[1].choices;

            if (baudrates.indexOf(baud) != -1) {
                data.answers.baud = baud;
                bot.reply(message, 'Setting baud rate to ' + baud + '. Use "update" to finalize changes to settings');
            } else {
                bot.reply(message, baud + ' is an invalid baud rate, the following baud rates are supported:');
                bot.reply(message, baudrates.join(', '));
            }
        });

        data.sparkedbot.hears(['list'], ['direct_message', 'direct_mention'], (bot, message) => {
            serialPort.listAsync().then(ports => ports)
                .then(ports => {

                var attachments = [];
                var attachment = {
                    title: 'Available Ports:',
                    color: '#FDD835',
                    fields: [],
                }

                ports.forEach(port => {
                    attachment.fields.push({
                        label: 'Field',
                        value: port.comName,
                        short: false,
                    })
                });

                attachments.push(attachment);

                bot.reply(message, {
                    text: '',
                    attachments: attachments,
                }, (err,resp) => {
                    //do nothing for now
                });
            })
        });

        data.sparkedbot.hears(['device (.*)', '(.*)port (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            var desiredPort = message.match[1];
            var found = false;
            serialPort.listAsync().then(ports => {
                ports.forEach( port => {
                    if(port.comName === desiredPort){
                        found = true;
                    }
                });
                if (found === false) {
                    bot.reply(message, 'Sorry, but no serial port exists with the name "' + desiredPort + '"');
                } else {
                    data.answers.port = desiredPort;
                    bot.reply(message, 'The port "' + desiredPort + '" is now the desired port. Use "update" to finalize changes to settings');
                }
            })
        });

        data.sparkedbot.hears(['file (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            var filepath = message.match[1];
            if (filepath.match(/https:\/\/raw\.githubusercontent\.com\/.*\.ino/g)){
                data.answers.filepath = filepath;
                bot.reply(message, 'Now watching the file ' + filepath);
                bot.reply(message, 'Make sure to add or change the github auth token by dm\'ing me with reauth [token] if the file is in a private repo that requires new permissions')
            } else {
                bot.reply(message, 'Sorry, but the url "' + filepath + '" does not seem like a valid raw github-hosted .ino file');
            }
        });

        data.sparkedbot.hears(['auth (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            if (message.event === 'direct_mention') {
                bot.reply(message, 'Well, if you\'re fine giving private repo privelage to everyone who can read this...');
            }
            var githubToken = message.match[1];
            data.answers.githubToken = githubToken;
            bot.reply(message, 'Setting GitHub auth token to "' + githubToken + '"');

        });

        data.sparkedbot.hears(['print (.*)', 'print\(\"(.*)\"\)'], ['direct_message', 'direct_mention'], (bot, message) => {
            // using print\(([\'\"])(.*)\1\) leads to an octal literal error in strict mode, and arduino uses double quotes instead of single quotes for printing string literals anyway
            var serialData = message.match[1];
            if (data.serialOpen) {
                bot.reply(message, 'Sent the following over serial: *' + serialData + '*');
                data.serialPort.write(serialData);
            } else {
                bot.reply(message, 'Sorry, the serial port has not opened yet. Data cannot be transferred until it is open');
                bot.reply(message, 'Do you have another application accessing this port? For example, the Arduino IDE\'s serial monitor will interfere with accessing the serial port');
            }
        });

        data.sparkedbot.hears(['start'], ['direct_message', 'direct_mention'], (bot, message) => {
            data.talk = true;
            if (data.serialOpen) {
                bot.reply(message, 'Ok, I\'ll write serial output here');
                data.serialPort.on('data', serialData => {
                    if (data.talk) {
                        bot.reply(message, '*Serial Output:* ' + serialData);
                        console.log(serialData);
                    }
                });
            } else {
                bot.reply(message, 'Sorry, the serial port has not opened yet. Data cannot be transferred until it is open');
                bot.reply(message, 'Do you have another application accessing this port? For example, the Arduino IDE\'s serial monitor will interfere with accessing the serial port');
            }
        });

        data.sparkedbot.hears(['stop'], ['direct_message', 'direct_mention'], (bot, message) => {
            data.talk = false;
            bot.reply(message, 'Ok. I\'ll stop writing serial output here.');
        });

        data.sparkedbot.hears(['quit', 'shut down'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'If you say so. Quitting the node process...');
            console.log('\n\nThe quit command was executed from slack, quitting...\n\n')
            process.exit(1);
        });
    }
    return data;
})

    .then(data => {

})

function upload(data) {
    new Promise(resolve => {
        if (data.forcedUpdate) {
            data.serialPort = new SerialPort(data.answers.port, {
                baudrate: data.answers.baud,
                parser: serialPort.parsers.readline("\n")
            }, true);
            data.forcedUpdate = false;
        }
        resolve();
    })
        .then(() => {
        return new Promise(resolve => {
            https.get(data.answers.filepath, res => {
                res.on('data', fileData => {
                    resolve(fileData);
                });
            });
        });
    })
        .then(fileData => {
        return new Promise(resolve => {
            fileData = fileData.toString('utf8');
            fs.writeFile(__dirname + 'downloadedFile.ino', fileData, function(err) {
                if(err) {
                    return console.log(err);
                }

                resolve();
            });
        });
    })
        .then(() => {
        var cmd = 'cat ' + __dirname + 'downloadedFile.ino';
        exec(cmd, function(error, stdout, stderr) {
            //console.log(stdout);
        });
    })
}

function getQuestions(ports) {
    if(ports){
        var portNames = ports.map(port => port.comName);
    }

    function getValue(value) {
        return answers => answers[ value ];
    }

    return [
        {
            type: 'list',
            name: 'port',
            message: 'Which serial port is the arduino connected to?',
            choices: portNames,
        },
        {
            type: 'list',
            name: 'baud',
            message: 'What baud rate should serial begin at?',
            choices: [
                '115200',
                '57600',
                '38400',
                '28800',
                '19200',
                '14400',
                '9600',
                '4800',
                '2400',
                '1200',
                '600',
                '300',
            ]
        },
        {
            type: 'input',
            name: 'filepath',
            message: 'Please enter the url to the raw file:\n',
            validate: value => {
                return true;
                if (value.match(/https:\/\/raw\.githubusercontent\.com\/.*\.ino/g)) {
                    return true;
                } else {
                    return 'Please enter a valid file path, e.g. https://raw.githubusercontent.com/USERNAME/REPO/BRANCH/FOLDER/FILE.ino';
                }
            }
        },
        {
            type: 'input',
            name: 'githubToken',
            message: 'If this is a private repo, please enter an auth code to view it:\n',
            validate: value => {
                return true;
                if (value.length === 40 || value.length === 0) {
                    return true;
                } else {
                    return 'Please enter a valid auth code';
                }
            }
        },
        {
            type: 'input',
            name: 'slackToken',
            message: 'If integrating with slack, please slack bot token:\n',
            validate: value => {
                return true;
                if (value.length === 41 || value.length === 0) {
                    return true;
                } else {
                    return 'Please enter a valid slack bot token';
                }
            }
        }
    ]
};
<commit_msg>feature, fix: added upload function, works (not including avrdude), also some fixes including with regex<commit_after>'use strict';
var Promise = require('bluebird');
Promise.longStackTraces();

var inquirer = require('inquirer');
var Botkit = require('botkit');
var serialPort = require('serialport');
var SerialPort = serialPort.SerialPort;
var http = require('http');
var https = require('https');
var fs = require('fs');
var exec = require('child_process').exec;

Promise.promisifyAll(inquirer);
Promise.promisifyAll(Botkit);
Promise.promisifyAll(serialPort);
Promise.promisifyAll(SerialPort);
Promise.promisifyAll(http);
Promise.promisifyAll(https);
Promise.promisifyAll(fs);
Promise.promisifyAll(exec);

serialPort.listAsync()
.then(ports => {
    return new Promise(resolve => {
        inquirer.prompt(getQuestions(ports), answers => {
            resolve(answers);
        });
    });
})
.then(answers => {
    var data = {
        answers: answers,
        forcedUpdate: false
    };

    if (data.answers.slackToken) {
        new Promise(resolve => {

            console.log('\n\nYour sparkedbot is being created!\nType "dosparked help" in slack to get a list of commands in slack\n\n');

            var Botkit = require('botkit');

            data.sparkedbot = Botkit.slackbot({
                debug: false,
                log: true
            });

            data.connect = function() {
                data.sparkedbot.spawn({
                    token: data.answers.slackToken,
                }).startRTM(err => {
                    if (err) {
                        if (!data.reconnects) {
                            data.reconnects = 1;
                        } else if (data.reconnects < 3) {

                        }
                        console.log('Could not connect to slack, retrying (' + data.reconnects + ')');
                    }
                });
            }

            data.connect();

            resolve(data);
        });
    }
    data.serialPort = new SerialPort(data.answers.port, {
        baudrate: 9600,//data.answers.baud,
        parser: serialPort.parsers.readline('\n')
    }, true);
    data.serialPort.on('open', () => {
        data.serialOpen = true;
    });

    return data;
})
.then(data => {
    if (data.sparkedbot) {

        data.sparkedbot.hears(['^help'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'Hello.');
        });

        data.sparkedbot.hears(['^example'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'Hello.');
        });

        data.sparkedbot.hears(['^status', '^current'], ['direct_message', 'direct_mention'], (bot, message) => {
            var attachments = [];
            var attachment = {
                title: 'Status:',
                color: '#FDD835',
                fields: [],
            }

            attachment.fields.push({
                label: 'Field',
                value: 'Current port: ' + data.answers.port,
                short: false,
            })

            attachment.fields.push({
                label: 'Field',
                value: 'Current baud rate: ' + data.answers.baud,
                short: false,
            })

            attachment.fields.push({
                label: 'Field',
                value: 'File being monitored: ' + data.answers.filepath,
                short: false,
            })

            attachments.push(attachment);

            bot.reply(message, {
                text: '',
                attachments: attachments,
            }, (err,resp) => {
                //do nothing for now
            });
        });

        data.sparkedbot.hears(['^refetch', '^reupload', '^upload', '^update'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'Refetching and uploading "' + data.answers.filepath + '"');
            data.forcedUpdate = true;
            upload(data)
            .then(cliOutput => {
                bot.reply(message, 'CLI output after uploading: ');
                bot.reply(message, '```' + cliOutput + '```');
            })
            .catch(e => {
                console.log('Caught an error thingy: ' + e);
            })
        });

        data.sparkedbot.hears(['^baud (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            var baud = message.match[1];
            var baudrates = getQuestions()[1].choices;

            if (baudrates.indexOf(baud) != -1) {
                data.answers.baud = baud;
                bot.reply(message, 'Setting baud rate to ' + baud + '. Use "update" to finalize changes to settings');
            } else {
                bot.reply(message, baud + ' is an invalid baud rate, the following baud rates are supported:');
                bot.reply(message, baudrates.join(', '));
            }
        });

        data.sparkedbot.hears(['^list'], ['direct_message', 'direct_mention'], (bot, message) => {
            serialPort.listAsync().then(ports => ports)
            .then(ports => {

                var attachments = [];
                var attachment = {
                    title: 'Available Ports:',
                    color: '#FDD835',
                    fields: [],
                }

                ports.forEach(port => {
                    attachment.fields.push({
                        label: 'Field',
                        value: port.comName,
                        short: false
                    })
                });

                attachments.push(attachment);

                bot.reply(message, {
                    text: '',
                    attachments: attachments,
                }, (err,resp) => {
                    //do nothing for now
                });
            })
        });

        data.sparkedbot.hears(['^port (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            var desiredPort = message.match[1];
            var found = false;
            serialPort.listAsync().then(ports => {
                ports.forEach( port => {
                    if(port.comName === desiredPort){
                        found = true;
                    }
                });
                if (found === false) {
                    bot.reply(message, 'Sorry, but no serial port exists with the name "' + desiredPort + '"');
                } else {
                    data.answers.port = desiredPort;
                    bot.reply(message, 'The port "' + desiredPort + '" is now the desired port. Use "update" to finalize changes to settings');
                }
            })
        });

        data.sparkedbot.hears(['^file (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            var filepath = message.match[1].slice(1, -1);
            if (filepath.match(/https:\/\/raw\.githubusercontent\.com\/.*\.ino/g)){
                data.answers.filepath = filepath;
                bot.reply(message, 'Now watching the file ' + filepath);
                bot.reply(message, 'Make sure to add or change the github auth token by dm\'ing me with reauth [token] if the file is in a private repo that requires new permissions')
            } else {
                bot.reply(message, 'Sorry, but the url "' + filepath + '" does not seem like a valid raw github-hosted .ino file');
            }
        });

        data.sparkedbot.hears(['^auth (.*)'], ['direct_message', 'direct_mention'], (bot, message) => {
            if (message.event === 'direct_mention') {
                bot.reply(message, 'Well, if you\'re fine giving private repo privelage to everyone who can read this...');
            }
            var githubToken = message.match[1];
            data.answers.githubToken = githubToken;
            bot.reply(message, 'Setting GitHub auth token to "' + githubToken + '"');

        });

        data.sparkedbot.hears(['^print (.*)', '^print\(\"(.*)\"\)'], ['direct_message', 'direct_mention'], (bot, message) => {
            // using print\(([\'\"])(.*)\1\) leads to an octal literal error in strict mode, and arduino uses double quotes instead of single quotes for printing string literals anyway
            var serialData = message.match[1];
            if (data.serialOpen) {
                bot.reply(message, 'Sent the following over serial: *' + serialData + '*');
                data.serialPort.write(serialData);
            } else {
                bot.reply(message, 'Sorry, the serial port has not opened yet. Data cannot be transferred until it is open');
                bot.reply(message, 'Do you have another application accessing this port? For example, the Arduino IDE\'s serial monitor will interfere with accessing the serial port');
            }
        });

        data.sparkedbot.hears(['^start'], ['direct_message', 'direct_mention'], (bot, message) => {
            data.talk = true;
            if (data.serialOpen) {
                bot.reply(message, 'Ok, I\'ll write serial output here');
                data.serialPort.on('data', serialData => {
                    if (data.talk) {
                        bot.reply(message, '*Serial Output:* ' + serialData);
                        console.log(serialData);
                    }
                });
            } else {
                bot.reply(message, 'Sorry, the serial port has not opened yet. Data cannot be transferred until it is open');
                bot.reply(message, 'Do you have another application accessing this port? For example, the Arduino IDE\'s serial monitor will interfere with accessing the serial port');
            }
        });

        data.sparkedbot.hears(['^stop'], ['direct_message', 'direct_mention'], (bot, message) => {
            data.talk = false;
            bot.reply(message, 'Ok. I\'ll stop writing serial output here.');
        });

        data.sparkedbot.hears(['^quit', '^shut down'], ['direct_message', 'direct_mention'], (bot, message) => {
            bot.reply(message, 'If you say so. Quitting the node process...');
            console.log('\n\nThe quit command was executed from slack, quitting...\n\n')
            process.exit(1);
        });
    }
    return data;
})
.then(data => {

})
.catch(e => {
    console.log('Error while starting sparked: ' + e);
})

function upload(data) {

    if (data.forcedUpdate) {
        data.serialPort = new SerialPort(data.answers.port, {
            baudrate: data.answers.baud,
            parser: serialPort.parsers.readline("\n")
        }, true);
    }

    var fileUploadProcess = new Promise( (resolve, reject) => {
        if (data.serialOpen) {
            https.get(data.answers.filepath, res => {
                res.on('data', fileData => {
                    resolve(fileData);
                });
            });
        } else {
            reject('Serial port is not yet open, cannot upload data');
        }
    })
    .then(fileData => {
        return new Promise((resolve, reject) => {
            fileData = fileData.toString('utf8');
            fs.writeFileAsync(__dirname + 'downloadedFile.ino', fileData)
            .done(content => {
                resolve();
            }, error => {
                reject(error);
            });
        });
    })
    .then(() => {
        return new Promise((resolve, reject) => {
            var cmd = 'cat ' + __dirname + 'downloadedFile.ino';
            exec(cmd, (error, stdout, stderr) => {
                if (error) {
                    reject(error);
                } else if (stderr) {
                    reject(stderr);
                }
                resolve(stdout);
            });
        });
    })
    .catch(e => {
        console.log('Error while uploading: [' + e + ']');
    })

    return fileUploadProcess.then().then().then(data => { return data });
}

function getQuestions(ports) {
    if(ports){
        var portNames = ports.map(port => port.comName);
    }

    function getValue(value) {
        return answers => answers[ value ];
    }

    return [
        {
            type: 'list',
            name: 'port',
            message: 'Which serial port is the arduino connected to?',
            choices: portNames,
        },
        {
            type: 'list',
            name: 'baud',
            message: 'What baud rate should serial begin at?',
            choices: [
                '115200',
                '57600',
                '38400',
                '28800',
                '19200',
                '14400',
                '9600',
                '4800',
                '2400',
                '1200',
                '600',
                '300',
            ]
        },
        {
            type: 'input',
            name: 'filepath',
            message: 'Please enter the url to the raw file:\n',
            validate: value => {
                return true;
                if (value.match(/https:\/\/raw\.githubusercontent\.com\/.*\.ino/g)) {
                    return true;
                } else {
                    return 'Please enter a valid file path, e.g. https://raw.githubusercontent.com/USERNAME/REPO/BRANCH/FOLDER/FILE.ino';
                }
            }
        },
        {
            type: 'input',
            name: 'githubToken',
            message: 'If this is a private repo, please enter an auth code to view it:\n',
            validate: value => {
                return true;
                if (value.length === 40 || value.length === 0) {
                    return true;
                } else {
                    return 'Please enter a valid auth code';
                }
            }
        },
        {
            type: 'input',
            name: 'slackToken',
            message: 'If integrating with slack, please slack bot token:\n',
            validate: value => {
                return true;
                if (value.length === 41 || value.length === 0) {
                    return true;
                } else {
                    return 'Please enter a valid slack bot token';
                }
            }
        }
    ]
};
<|endoftext|>


<commit_before>/**
 * Initialize your data structure here.
 */
var WordDictionary = function() {
    this.isWord = false;
    this.links = Array.from({length: 26}, (v) => null);
};

/**
 * Adds a word into the data structure.
 * @param {string} word
 * @return {void}
 */
WordDictionary.prototype.addWord = function(word) {
    let curr = this;
    for (let i = 0, len = word.length; i < len; ++i) {
        let j = word.charCodeAt(i) - 97;
        if (curr.links[j] === null) {
            curr.links[j] = new WordDictionary;
        }
        curr = curr.links[j];
    }
    curr.isWord = true;
};

/**
 * Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter.
 * @param {string} word
 * @return {boolean}
 */
WordDictionary.prototype.search = function(word) {
    // Search down from the current node for the partial word
    // Return if found
    var recDFSHelper = function(node, partWord) {
        if (partWord.length === 0) {
            return node.isWord;
        }
        let code = partWord.charCodeAt(0);
        if (code === 46) { // wildcard '.'
            for(let j = 0, len = node.links.length; j < len; ++j) {
                if (node.links[j] !== null) {
                    if (recDFSHelper(node.links[j], partWord.substring(1))) {
                        return true;
                    }
                }
            }
            return false;
        }
        else {
            return (node.links[code - 97] !== null) &&
                    recDFSHelper(node.links[code - 97], partWord.substring(1));
        }
    };

    return recDFSHelper(this, word);
};

/**
 * Your WordDictionary object will be instantiated and called as such:
 * var obj = Object.create(WordDictionary).createNew()
 * obj.addWord(word)
 * var param_2 = obj.search(word)
 */
var dict = new WordDictionary;
dict.addWord("bad");
dict.addWord("dad");
dict.addWord("mad");
console.log("search '' ->", dict.search(""));
console.log("search 'ma' ->", dict.search("ma"));
console.log("search 'pad' ->", dict.search("pad"));
console.log("search 'dad' ->", dict.search("dad"));
console.log("search 'date' ->", dict.search("date"));
console.log("search '.ad' ->", dict.search(".ad"));
console.log("search 'b..' ->", dict.search("b.."));
<commit_msg>August Leetcoding Challenge: Revisited p211m Add an Search Word<commit_after>/**
 * Initialize your data structure here.
 */
var WordDictionary = function () {
  this.isWord = false;
  this.links = Array.from({ length: 26 }, (v) => null);
};

/**
 * Adds a word into the data structure.
 * @param {string} word
 * @return {void}
 */
WordDictionary.prototype.addWord = function (word) {
  let curr = this;
  for (let i = 0; i < word.length; ++i) {
    let j = word.charCodeAt(i) - 97;
    if (!curr.links[j]) curr.links[j] = new WordDictionary();
    curr = curr.links[j];
  }
  curr.isWord = true;
};

/**
 * Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter.
 * @param {string} word
 * @return {boolean}
 */
WordDictionary.prototype.search = function (word) {
  // Search down from the current node for the partial word
  // Return if found
  var recDFSHelper = function (node, partWord) {
    if (partWord.length === 0) return node.isWord;
    let code = partWord.charCodeAt(0);
    if (code === 46) {
      // wildcard '.'
      for (let j = 0, len = node.links.length; j < len; ++j) {
        if (node.links[j] && recDFSHelper(node.links[j], partWord.substring(1)))
          return true;
      }
      return false;
    } else {
      return (
        node.links[code - 97] !== null &&
        recDFSHelper(node.links[code - 97], partWord.substring(1))
      );
    }
  };

  return recDFSHelper(this, word);
};

/**
 * Your WordDictionary object will be instantiated and called as such:
 * var obj = Object.create(WordDictionary).createNew()
 * obj.addWord(word)
 * var param_2 = obj.search(word)
 */
var dict = new WordDictionary();
dict.addWord('bad');
dict.addWord('dad');
dict.addWord('mad');
console.log("search '' ->", dict.search(''));
console.log("search 'ma' ->", dict.search('ma'));
console.log("search 'pad' ->", dict.search('pad'));
console.log("search 'dad' ->", dict.search('dad'));
console.log("search 'date' ->", dict.search('date'));
console.log("search '.ad' ->", dict.search('.ad'));
console.log("search 'b..' ->", dict.search('b..'));
<|endoftext|>


<commit_before>document.addEventListener( "DOMContentLoaded", function( e ){

  Butter({
    config: "butter/config.json",
    ready: function( butter ){
      var media = butter.media[ 0 ];

      function start(){
        media.addTrack( "Track1" );
        media.addTrack();
        media.addTrack();

        butter.tracks[0].addTrackEvent({
          type: "text",
          popcornOptions: {
            start: 0,
            end: 1,
            text: "This is a test.",
            target: "Area1"
          }
        });

      } //start

      media.onReady( start );

    }
  }); //Butter

}, false );
<commit_msg>[#1516] Added better default track events for robots template<commit_after>document.addEventListener( "DOMContentLoaded", function( e ){

  Butter({
    config: "butter/config.json",
    ready: function( butter ){
      var media = butter.media[ 0 ];

      function start(){
        var trackOne = media.addTrack( "Track0" ),
            trackTwo = media.addTrack(),
            trackThree = media.addTrack();

        trackOne.addTrackEvent({
          "type": "flickr",
          "popcornOptions": {
            "start": 33.06,
            "end": 37.873,
            "text": "This is a test.",
            "target": "top-title",
            "tags": "kitten",
            "height": "200",
            "width": "200",
            "padding": "0",
            "border": "0",
            "numberofimages": "10"
          }
        });

        trackOne.addTrackEvent({
          "type": "titles",
          "popcornOptions": {
            "start": 0,
            "end": 2.361,
            "target": "top-title",
            "text": "ALL HAIL ROBOTS!",
            "styles": "Top Title",
            "transitionSpeed": "slow"
          }
        });

        trackOne.addTrackEvent({
          type: "titles",
          popcornOptions: {
            start: 57.059,
            end: 62.146,
            target: "top-title",
            text: "Share on Twitter! Use hashtag #allhailrobots",
            styles: "Top Title",
            transitionSpeed: "normal"
          }
        });

        trackTwo.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 5.161,
            "end": 7.454,
            "target": "top-title",
            "text": "The human beings",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "100",
            "speed": "100",
            "pluginPath": "../shared/plugins/speak/"
          }
        });

        trackTwo.addTrackEvent({
          "type": "googlemap",
          "popcornOptions": {
            "start": 18.83169,
            "end": 24.41145,
            "target": "video-overlay",
            "type": "STAMEN-TONER",
            "zoom": 10,
            "lat": "49.264810770785395",
            "lng": "-123.2402695734375",
            "location": "",
            "heading": "",
            "pitch": 1
          }
        });
        trackTwo.addTrackEvent({
          "type": "zoink",
          "popcornOptions": {
            "start": 30.49485575863204,
            "end": 32.9224219865747,
            "target": "top-title",
            "text": "Gotta dance!",
            "type": "thought",
            "triangle": "top left",
            "flip": true,
            "classes": "fx",
            "order": "1",
            "top": "200",
            "left": "275",
            "width": "200"
          }
        });
        trackTwo.addTrackEvent({
          "type": "photo",
          "popcornOptions": {
            "start": 24.481196999999998,
            "end": 30.060957,
            "target": "video-overlay",
            "src": "http://media.techworld.com/cmsdata/news/3213248/chucknorris.jpg",
            "href": "",
            "width": "500",
            "height": "500",
            "top": "100",
            "left": "200"
          }
        });

        trackThree.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 7.323,
            "end": 9.198,
            "target": "top-title",
            "text": "so very weak",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "100",
            "speed": "120",
            "pluginPath": "../shared/plugins/speak/"
          }
        });
        trackThree.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 10.881,
            "end": 12.158,
            "target": "top-title",
            "text": "We robots have come to install order on earth.  Our invasion will be centered at this location",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "100",
            "speed": "60",
            "pluginPath": "../shared/plugins/speak/"
          }
        });
        trackThree.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 32.96156578783416,
            "end": 35.02191450484101,
            "target": "top-title",
            "text": "the only thing that can stop them is this object",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "70",
            "speed": "70",
            "pluginPath": "../shared/plugins/speak/"
          }
        });
        trackThree.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 24.551,
            "end": 29.642,
            "target": "top-title",
            "text": "We have come for this human - he is strong",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "20",
            "speed": "120",
            "pluginPath": "../shared/plugins/speak/"
          }
        });
        trackThree.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 54.609,
            "end": 55.609,
            "target": "top-title",
            "text": "YOU MUST NOW TELL THE OTHERS OF OUR CONQUEST!",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "80",
            "speed": "80",
            "pluginPath": "../shared/plugins/speak/"
          }
        });
        trackThree.addTrackEvent({
          "type": "speak",
          "popcornOptions": {
            "start": 38.082,
            "end": 41.081,
            "target": "top-title",
            "text": "we must destroy them all",
            "showText": false,
            "amplitude": "",
            "wordgap": "0",
            "pitch": "0",
            "speed": "0",
            "pluginPath": "../shared/plugins/speak/"
          }
        });

      } //start

      media.onReady( start );

    }
  }); //Butter

}, false );
<|endoftext|>


<commit_before>var if_height,
    restauration_iframe = jQuery('#epfl-restauration');

window.addEventListener('message', function(e)
{

    var h = Number( e.data.replace( /.*if_height=(\d+)(?:&|$)/, '$1' ) );

    if (!isNaN( h ) && h > 0 && h !== if_height) {
        /* Height has changed, update the iframe */
        if_height = h;
        restauration_iframe.height(h);
    }

} , false);<commit_msg>Add comment in script<commit_after>var if_height,
    restauration_iframe = jQuery('#epfl-restauration');

/*
* We add a listener to receive messages sent by iframe containing menu list. Messages tells the iframe's content height.
* This will be used to resize iframe (if size has changed) */
window.addEventListener('message', function(e)
{

    var h = Number( e.data.replace( /.*if_height=(\d+)(?:&|$)/, '$1' ) );

    if (!isNaN( h ) && h > 0 && h !== if_height) {
        /* Height has changed, update the iframe */
        if_height = h;
        restauration_iframe.height(h);
    }

} , false);<|endoftext|>