<commit_before>//
// Vows.js - asynchronous promise-based testing for node.js
//
//   usage:
//
//       var vows = require('vows'),
//           assert = require('assert');
//
//       vows.tell('Deep Thought', function () {
//           question('what is the answer to the universe?').addVow(function (answer) {
//               assert.equals(answer, 42);
//           }, 'it should know the answer to the ultimate question of life');
//       });
//
var path = require('path');

require.paths.unshift(path.join(path.dirname(__filename), 'vendor'),
                      path.dirname(__filename));

var sys = require('sys'),
    assert = require('assert'),
    events = require('events'),
    eyes = require('eyes').inspector({ writer: null }),
    vows = exports;

// Keeps track of the outcome of vows.
var total  = 0, honored = 0,
    broken = 0, errored = 0,
    start, end;

//
// Assertion Macros
//
vows.macros = require('vows/macros');

assert.AssertionError.prototype.toString = function () {
    var that = this,
        source = this.stack.match(/(\w+\.js)(:\d+):\d+/);

    function parse(str) {
        return str.replace(/{actual}/g,   inspect(that.actual)).
                   replace(/{expected}/g, inspect(that.expected)).
                   replace(/{operator}/g, stylize(that.operator, 'bold'));
    }

    if (this.message) {
        return stylize(parse(this.message) + " @ " +
                       stylize(source[1], 'bold') + source[2], 'yellow');
    } else {
        return stylize([
            this.expected,
            this.operator,
            this.actual
        ].join(' '), 'yellow');
    }
}

//
// This function gets added to events.Promise.prototype, by default.
// It's essentially a wrapper around `addCallback`, which adds all the specification
// goodness.
//
function addVow(/* description & callback */) {
    var desc, callback;

    total++;

    if (arguments.length > 1) {
        // Sometimes it might be nicer to pass the proof first,
        // and the description second, so we let the user choose
        // the order.
        if (arguments[0] instanceof Function) {
            callback = arguments[0];
            desc     = arguments[1];
        } else {
            desc     = arguments[0];
            callback = arguments[1];
        }
    } else if (arguments[0].constructor.name === 'Vow') {
        callback = arguments[0].callback;
        desc     = arguments[0].description;
    } else {
        throw new(Error)("wrong argument type for addVow()");
    }

    return this.addCallback(function () {
        var vow = "- ", exception, topic, msg;

        // Run the test, and try to catch `AssertionError`s and other exceptions;
        // increment counters accordingly.
        try {
            callback.apply(null, arguments);
            vow += stylize(desc, 'green');
            honored++;
        } catch (e) {
            if (e.name && e.name.match(/AssertionError/)) {
                vow += stylize(desc, 'yellow');
                exception = '  ~ ' + e.toString() + "\n";
                broken++;
            } else {
                vow += stylize(desc, 'red');
                msg = e.stack || e.message || e.toString() || e;
                exception = '  ! ' + stylize(msg, 'red') + "\n";
                errored++;
            }
        }
        sys.puts(vow);

        if (exception) sys.puts(exception);

        tryFinish(vows.remaining);
    });
};

function tryFinish(remaining) {
    // Output results once all the vows have been checked
    if (honored + broken + errored === total && remaining === 0) {
        var result = honored + " honored, " +
                     broken  + " broken, "  +
                     errored + " errored",

        style = honored === total ?
            ('green') : (errored === 0 ? 'yellow' : 'red');

        sys.puts("\nVerified " + total + " vows in " +
                (((new(Date)) - start) / 1000) + " seconds.");

        sys.puts("\n" + stylize(result, style));
    }
}

// Options
vows.options = {
    Emitter: events.Promise
};

// Run all vows/tests
vows.tell = function (topic, tests) {
    this.options.Emitter.prototype.addVow = addVow;
    this.tests = tests;
    this.remaining = 0;

    function Context(vow, topics) {
        this.tests = vow.callback;
        this.topics = topics || [];
        this.name = vow.description || null;
    }

    function Vow(callback, description) {
        this.callback = callback;
        this.description = description;
    }

    return process.nextTick(function () {
        var setup, vow;

        if (typeof topic === 'string' && tests) sys.puts('\n' + stylize(topic, 'underline') + '\n');
        else if (topic instanceof Object) tests = topic;
        else throw "tell() takes a topic and an Object";

        start = new(Date);

        if (typeof(vows.tests) === 'function') {
            return vows.tests.call(null);
        } else {
            (function count(tests) {
                vows.remaining++;
                Object.keys(tests).forEach(function (key) {
                    if (! (tests[key] instanceof Function)) count(tests[key]);
                });
            })(vows.tests);

            (function run(ctx) {
                if (typeof(ctx.tests["setup"]) === 'function') {
                    setup = ctx.tests.setup.apply(this, ctx.topics);

                    if (! (setup instanceof vows.options.Emitter)) {
                        var emitter = new(vows.options.Emitter);

                        process.nextTick(function (val) {
                            return function () { emitter.emitSuccess(val) };
                        }(setup)); setup = emitter;
                    }
                } else { setup = null }

                Object.keys(ctx.tests).filter(function (k) {
                    return ctx.tests[k] && k !== 'setup';
                }).forEach(function (item) {
                    vow = new(Vow)(ctx.tests[item], ctx.name ? ctx.name + ' ' + item : item);

                    if (vow.callback instanceof Function) {
                        setup.addVow(vow);
                    } else if (typeof(vow.callback) === 'object' && ! Array.isArray(vow.callback)) {
                        if (setup) {
                            setup.addCallback(function (vow, ctx) {
                                return function (val) {
                                    ctx.topics.unshift(val);
                                    return run(new(Context)(vow, ctx.topics));
                                };
                            }(vow, ctx));
                        } else {
                            run(new(Context)(vow, ctx.topics));
                        }
                    }
                });
                tryFinish(--vows.remaining);
            })(new(Context)(new(Vow)(vows.tests, null), []));
        }
    });
};

// Return the `vows` object after setting some options
vows.config = function (opts) {
    process.mixin(this.options, opts);
    return this;
};

//
// Utility functions
//

function inspect(val) {
    return '\033[1m' + eyes(val) + '\033[22m';
}

// Stylize a string
function stylize(str, style) {
    var styles = {
        'bold'      : [1,  22],
        'underline' : [4,  24],
        'yellow'    : [33, 39],
        'green'     : [32, 39],
        'red'       : [31, 39]
    };
    return '\033[' + styles[style][0] + 'm' + str +
           '\033[' + styles[style][1] + 'm';
}

<commit_msg>style changes<commit_after>//
// Vows.js - asynchronous promise-based testing for node.js
//
//   usage:
//
//       var vows = require('vows'),
//           assert = require('assert');
//
//       vows.tell('Deep Thought', function () {
//           question('what is the answer to the universe?').addVow(function (answer) {
//               assert.equals(answer, 42);
//           }, 'it should know the answer to the ultimate question of life');
//       });
//
var path = require('path');

require.paths.unshift(path.join(path.dirname(__filename), 'vendor'),
                      path.dirname(__filename));

var sys = require('sys'),
    assert = require('assert'),
    events = require('events'),
    eyes = require('eyes').inspector({ writer: null }),
    vows = exports;

// Keeps track of the outcome of vows.
var total  = 0, honored = 0,
    broken = 0, errored = 0,
    start, end;

//
// Assertion Macros
//
vows.macros = require('vows/macros');

assert.AssertionError.prototype.toString = function () {
    var that = this,
        source = this.stack.match(/(\w+\.js)(:\d+):\d+/);

    function parse(str) {
        return str.replace(/{actual}/g,   inspect(that.actual)).
                   replace(/{expected}/g, inspect(that.expected)).
                   replace(/{operator}/g, stylize(that.operator, 'bold'));
    }

    if (this.message) {
        return stylize(parse(this.message) + " @ " +
                       stylize(source[1], 'bold') + source[2], 'yellow');
    } else {
        return stylize([
            this.expected,
            this.operator,
            this.actual
        ].join(' '), 'yellow');
    }
}

//
// This function gets added to events.Promise.prototype, by default.
// It's essentially a wrapper around `addCallback`, which adds all the specification
// goodness.
//
function addVow(/* description & callback */) {
    var desc, callback;

    total++;

    if (arguments.length > 1) {
        // Sometimes it might be nicer to pass the proof first,
        // and the description second, so we let the user choose
        // the order.
        if (arguments[0] instanceof Function) {
            callback = arguments[0], desc = arguments[1];
        } else {
            callback = arguments[1], desc = arguments[0];
        }
    } else if (arguments[0].constructor.name === 'Vow') {
        callback = arguments[0].callback,
        desc = arguments[0].description;
    } else {
        throw new(Error)("wrong argument type for addVow()");
    }

    return this.addCallback(function () {
        var vow = "- ", exception, topic, msg;

        // Run the test, and try to catch `AssertionError`s and other exceptions;
        // increment counters accordingly.
        try {
            callback.apply(null, arguments);
            vow += stylize(desc, 'green');
            honored++;
        } catch (e) {
            if (e.name && e.name.match(/AssertionError/)) {
                vow += stylize(desc, 'yellow');
                exception = '  ~ ' + e.toString() + "\n";
                broken++;
            } else {
                vow += stylize(desc, 'red');
                msg = e.stack || e.message || e.toString() || e;
                exception = '  ! ' + stylize(msg, 'red') + "\n";
                errored++;
            }
        }
        sys.puts(vow);

        if (exception) sys.puts(exception);

        tryFinish(vows.remaining);
    });
};

function tryFinish(remaining) {
    // Output results once all the vows have been checked
    if (honored + broken + errored === total && remaining === 0) {
        var result = honored + " honored, " +
                     broken  + " broken, "  +
                     errored + " errored",

        style = honored === total ?
            ('green') : (errored === 0 ? 'yellow' : 'red');

        sys.puts("\nVerified " + total + " vows in " +
                (((new(Date)) - start) / 1000) + " seconds.");

        sys.puts("\n" + stylize(result, style));
    }
}

// Options
vows.options = {
    Emitter: events.Promise
};

// Run all vows/tests
vows.tell = function (topic, tests) {
    this.options.Emitter.prototype.addVow = addVow;
    this.tests = tests;
    this.remaining = 0;

    function Context(vow, topics) {
        this.tests = vow.callback;
        this.topics = topics || [];
        this.name = vow.description || null;
    }

    function Vow(callback, description) {
        this.callback = callback;
        this.description = description;
    }

    return process.nextTick(function () {
        var setup, vow;

        if (typeof topic === 'string' && tests) sys.puts('\n' + stylize(topic, 'underline') + '\n');
        else if (topic instanceof Object) tests = topic;
        else throw "tell() takes a topic and an Object";

        start = new(Date);

        if (typeof(vows.tests) === 'function') {
            return vows.tests.call(null);
        } else {
            (function count(tests) {
                vows.remaining++;
                Object.keys(tests).forEach(function (key) {
                    if (! (tests[key] instanceof Function)) count(tests[key]);
                });
            })(vows.tests);

            (function run(ctx) {
                if (typeof(ctx.tests["setup"]) === 'function') {
                    setup = ctx.tests.setup.apply(this, ctx.topics);

                    if (! (setup instanceof vows.options.Emitter)) {
                        var emitter = new(vows.options.Emitter);

                        process.nextTick(function (val) {
                            return function () { emitter.emitSuccess(val) };
                        }(setup)); setup = emitter;
                    }
                } else { setup = null }

                Object.keys(ctx.tests).filter(function (k) {
                    return ctx.tests[k] && k !== 'setup';
                }).forEach(function (item) {
                    vow = new(Vow)(ctx.tests[item], ctx.name ? ctx.name + ' ' + item : item);

                    if (vow.callback instanceof Function) {
                        setup.addVow(vow);
                    } else if (typeof(vow.callback) === 'object' && ! Array.isArray(vow.callback)) {
                        if (setup) {
                            setup.addCallback(function (vow, ctx) {
                                return function (val) {
                                    ctx.topics.unshift(val);
                                    return run(new(Context)(vow, ctx.topics));
                                };
                            }(vow, ctx));
                        } else {
                            run(new(Context)(vow, ctx.topics));
                        }
                    }
                });
                tryFinish(--vows.remaining);
            })(new(Context)(new(Vow)(vows.tests, null), []));
        }
    });
};

// Return the `vows` object after setting some options
vows.config = function (opts) {
    process.mixin(this.options, opts);
    return this;
};

//
// Utility functions
//

function inspect(val) {
    return '\033[1m' + eyes(val) + '\033[22m';
}

// Stylize a string
function stylize(str, style) {
    var styles = {
        'bold'      : [1,  22],
        'underline' : [4,  24],
        'yellow'    : [33, 39],
        'green'     : [32, 39],
        'red'       : [31, 39]
    };
    return '\033[' + styles[style][0] + 'm' + str +
           '\033[' + styles[style][1] + 'm';
}

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */

import * as tf from '@tensorflow/tfjs';

const canvas = document.getElementById('canvas');
const order = 3;
// Convert world coordinates to canvas ones.
function world2canvas(canvas, x, y) {
  return [x + canvas.width / 2, -y + canvas.height / 2];
}
// Draw x and y axes in the canvas.
function drawAxes(canvas) {
  const ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath();
  const leftCoord = world2canvas(canvas, -canvas.width / 2, 0);
  const rightCoord = world2canvas(canvas, canvas.width / 2, 0);
  ctx.moveTo(leftCoord[0], leftCoord[1]);
  ctx.lineTo(rightCoord[0], rightCoord[1]);
  ctx.stroke();
  const topCoord = world2canvas(canvas, 0, canvas.height / 2);
  const bottomCoord = world2canvas(canvas, 0, -canvas.height / 2);
  ctx.moveTo(topCoord[0], topCoord[1]);
  ctx.lineTo(bottomCoord[0], bottomCoord[1]);
  ctx.stroke();
}

// Draw x and y data in the canvas.
//
// Also draws the x and y axes.
//
// Args:
//   canvas: The canvas to draw the data in.
//   xyData: An Array of [x, y] Arrays.
function drawXYData(canvas, xyData) {
  drawAxes(canvas);
  const ctx = canvas.getContext('2d');
  for (let i = 0; i < xyData.length; ++i) {
    ctx.beginPath();
    const x = xyData[i][0];
    const y = xyData[i][1];
    const canvasCoord = world2canvas(canvas, x, y);
    ctx.arc(canvasCoord[0], canvasCoord[1], 4, 0, Math.PI * 2, true);
    ctx.stroke();
  }
}

// Calculate the arithmetic mean of a vector.
//
// Args:
//   vector: The vector represented as an Array of Numbers.
//
// Returns:
//   The arithmetic mean.
function mean(vector) {
  let sum = 0;
  for (const x of vector) {
    sum += x;
  }
  return sum / vector.length;
}

// Calculate the standard deviation of a vector.
//
// Args:
//   vector: The vector represented as an Array of Numbers.
//
// Returns:
//   The standard deviation.
function stddev(vector) {
  let squareSum = 0;
  const vectorMean = mean(vector);
  for (const x of vector) {
    squareSum += (x - vectorMean) * (x - vectorMean);
  }
  return Math.sqrt(squareSum / (vector.length - 1));
}

// Normalize a vector by its mean and standard deviation.
function normalizeVector(vector, vectorMean, vectorStddev) {
  return vector.map(x => (x - vectorMean) / vectorStddev);
}

// Convert x-y data to normalized Tensors.
//
// Args:
//   xyData: An Array of [x, y] Number Arrays.
//   order: The order of the polynomial to generate data for. Assumed to be
//     a non-negative integer.
//
// Returns: An array consisting of the following
//   xPowerMeans: Arithmetic means of the powers of x, from order `1` to
//      order `order`
//   xPowerStddevs: Standard deviations of the powers of x.
//   Normalized powers of x: an Tensor2D of shape [batchSize, order + 1].
//     The first column is all ones; the following columns are powers of x
//     from order `1` to `order`.
//   yMean: Arithmetic mean of y.
//   yStddev: Standard deviation of y.
//   Normalized powers of y: an Tensor2D of shape [batchSize, 1].
function toNormalizedTensors(xyData, order) {
  const batchSize = xyData.length;
  const data = [];
  const xData = xyData.map(xy => xy[0]);
  const yData = xyData.map(xy => xy[1]);
  const yMean = mean(yData);
  const yStddev = stddev(yData);
  const yNormalized = normalizeVector(yData, yMean, yStddev);
  const normalizedXPowers = [];
  const xPowerMeans = [];
  const xPowerStddevs = [];
  for (let i = 0; i < order; ++i) {
    const xPower = xData.map(x => Math.pow(x, i + 1));
    const xPowerMean = mean(xPower);
    xPowerMeans.push(xPowerMean);
    const xPowerStddev = stddev(xPower);
    xPowerStddevs.push(xPowerStddev);
    const normalizedXPower = normalizeVector(xPower, xPowerMean, xPowerStddev);
    normalizedXPowers.push(normalizedXPower);
  }
  const xArrayData = [];
  for (let i = 0; i < xData.length; ++i) {
    for (let j = 0; j < order + 1; ++j) {
      if (j === 0) {
        xArrayData.push(1);
      } else {
        xArrayData.push(normalizedXPowers[j - 1][i]);
      }
    }
  }
  return [
    xPowerMeans, xPowerStddevs, tf.tensor2d(xArrayData, [batchSize, order + 1]),
    yMean, yStddev, tf.tensor2d(yNormalized, [batchSize, 1])
  ];
}

// Fit a model for polynomial regression.
//
// Args:
//   xyData: An Array of [x, y] Number Arrays.
//   epochs: How many epochs to train for.
//   learningRate: Learning rate.
//
// Returns: An Array consiting of the following:
//   The trained keras Model instance.
//   xPowerMeans: Arithmetic means of the powers of x, from order `1` to
//      order `order`
//   xPowerStddevs: Standard deviations of the powers of x.
//   yMean: Arithmetic mean of y.
//   yStddev: Standard deviation of y.
async function fitModel(xyData, epochs, learningRate) {
  const batchSize = xyData.length;
  const outputs = toNormalizedTensors(xyData, order);
  const xPowerMeans = outputs[0];
  const xPowerStddevs = outputs[1];
  const xData = outputs[2];
  const yMean = outputs[3];
  const yStddev = outputs[4];
  const yData = outputs[5];
  const input = tf.input({shape: [order + 1]});
  const linearLayer =
      tf.layers.dense({units: 1, kernelInitializer: 'Zeros', useBias: false});
  const output = linearLayer.apply(input);
  const model = tf.model({inputs: input, outputs: output});
  const sgd = tf.train.sgd(learningRate);
  model.compile({optimizer: sgd, loss: 'meanSquaredError'});
  await model.fit(xData, yData, {
    batchSize: batchSize,
    epochs: epochs,
  });
  console.log(
      'Model weights (normalized):',
      model.trainableWeights[0].read().dataSync());
  return [model, xPowerMeans, xPowerStddevs, yMean, yStddev];
}

// Render the predictions made by the model.
function renderModelPredictions(
    canvas, order, model, xPowerMeans, xPowerStddevs, yMean, yStddev) {
  const ctx = canvas.getContext('2d');
  const width = canvas.width;
  let x = -0.5 * width;
  const xStep = 0.02 * width;
  const xs = [];
  const xPowers = [];
  let n = 0;
  while (x < 0.5 * width) {
    xs.push(x);
    let d = 1;
    for (let j = 0; j < order + 1; ++j) {
      xPowers.push(
          j === 0 ? d : ((d - xPowerMeans[j - 1]) / xPowerStddevs[j - 1]));
      d *= x;
    }
    x += xStep;
    n++;
  }

  const predictOut = model.predict(tf.tensor2d(xPowers, [n, order + 1]));
  const normalizedYs = predictOut.dataSync();
  ctx.beginPath();
  let canvasXY = world2canvas(canvas, xs[0], normalizedYs[0] * yStddev + yMean);
  ctx.moveTo(canvasXY[0], canvasXY[1]);
  for (let i = 1; i < n; ++i) {
    canvasXY = world2canvas(canvas, xs[i], normalizedYs[i] * yStddev + yMean);
    ctx.lineTo(canvasXY[0], canvasXY[1]);
    ctx.stroke();
  }
}

// Generate x-y data based on the size of the canvas.
function generateXYData(canvas, coeffs) {
  const data = [];
  for (let x = -canvas.width / 2; x < canvas.width / 2;
       x += canvas.width / 25) {
    data.push([
      x, coeffs[0] * x * x * x + coeffs[1] * x * x + coeffs[2] * x + coeffs[3]
    ]);
  }
  return data;
}

// Fit a model and render the data and predictions.
async function fitAndRender() {
  const epochs = +epochsElement.value;
  const learningRate = +learningRateElement.value;
  if (!isFinite(epochs) || !isFinite(learningRate)) {
    return;
  }
  const coeffs = [
    +cubicCoeffElement.value, +quadCoeffElement.value,
    +linearCoeffElement.value, +constCoeffElement.value
  ];
  console.log('True coefficients: ' + JSON.stringify(coeffs));
  let xyData = generateXYData(canvas, coeffs);
  drawXYData(canvas, xyData);
  const fitOutputs = await fitModel(xyData, epochs, learningRate);
  const model = fitOutputs[0];
  const xPowerMeans = fitOutputs[1];
  const xPowerStddevs = fitOutputs[2];
  const yMean = fitOutputs[3];
  const yStddev = fitOutputs[4];
  await renderModelPredictions(
      canvas, order, model, xPowerMeans, xPowerStddevs, yMean, yStddev);
}

const cubicCoeffElement = document.getElementById('cubic-coeff');
const quadCoeffElement = document.getElementById('quad-coeff');
const linearCoeffElement = document.getElementById('linear-coeff');
const constCoeffElement = document.getElementById('const-coeff');

const epochsElement = document.getElementById('epochs');
const learningRateElement = document.getElementById('learning-rate');

cubicCoeffElement.addEventListener('keyup', fitAndRender);
quadCoeffElement.addEventListener('keyup', fitAndRender);
linearCoeffElement.addEventListener('keyup', fitAndRender);
constCoeffElement.addEventListener('keyup', fitAndRender);

epochsElement.addEventListener('keyup', fitAndRender);
learningRateElement.addEventListener('keyup', fitAndRender);

fitAndRender();
<commit_msg>Update index.js (#354)<commit_after>/**
 * @license
 * Copyright 2018 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */

import * as tf from '@tensorflow/tfjs';

const canvas = document.getElementById('canvas');
const order = 3;
// Convert world coordinates to canvas ones.
function world2canvas(canvas, x, y) {
  return [x + canvas.width / 2, -y + canvas.height / 2];
}
// Draw x and y axes in the canvas.
function drawAxes(canvas) {
  const ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath();
  const leftCoord = world2canvas(canvas, -canvas.width / 2, 0);
  const rightCoord = world2canvas(canvas, canvas.width / 2, 0);
  ctx.moveTo(leftCoord[0], leftCoord[1]);
  ctx.lineTo(rightCoord[0], rightCoord[1]);
  ctx.stroke();
  const topCoord = world2canvas(canvas, 0, canvas.height / 2);
  const bottomCoord = world2canvas(canvas, 0, -canvas.height / 2);
  ctx.moveTo(topCoord[0], topCoord[1]);
  ctx.lineTo(bottomCoord[0], bottomCoord[1]);
  ctx.stroke();
}

// Draw x and y data in the canvas.
//
// Also draws the x and y axes.
//
// Args:
//   canvas: The canvas to draw the data in.
//   xyData: An Array of [x, y] Arrays.
function drawXYData(canvas, xyData) {
  drawAxes(canvas);
  const ctx = canvas.getContext('2d');
  for (let i = 0; i < xyData.length; ++i) {
    ctx.beginPath();
    const x = xyData[i][0];
    const y = xyData[i][1];
    const canvasCoord = world2canvas(canvas, x, y);
    ctx.arc(canvasCoord[0], canvasCoord[1], 4, 0, Math.PI * 2, true);
    ctx.stroke();
  }
}

// Calculate the arithmetic mean of a vector.
//
// Args:
//   vector: The vector represented as an Array of Numbers.
//
// Returns:
//   The arithmetic mean.
function mean(vector) {
  let sum = 0;
  for (const x of vector) {
    sum += x;
  }
  return sum / vector.length;
}

// Calculate the standard deviation of a vector.
//
// Args:
//   vector: The vector represented as an Array of Numbers.
//
// Returns:
//   The standard deviation.
function stddev(vector) {
  let squareSum = 0;
  const vectorMean = mean(vector);
  for (const x of vector) {
    squareSum += (x - vectorMean) * (x - vectorMean);
  }
  return Math.sqrt(squareSum / (vector.length - 1));
}

// Normalize a vector by its mean and standard deviation.
function normalizeVector(vector, vectorMean, vectorStddev) {
  return vector.map(x => (x - vectorMean) / vectorStddev);
}

// Convert x-y data to normalized Tensors.
//
// Args:
//   xyData: An Array of [x, y] Number Arrays.
//   order: The order of the polynomial to generate data for. Assumed to be
//     a non-negative integer.
//
// Returns: An array consisting of the following
//   xPowerMeans: Arithmetic means of the powers of x, from order `1` to
//      order `order`
//   xPowerStddevs: Standard deviations of the powers of x.
//   Normalized powers of x: an Tensor2D of shape [batchSize, order + 1].
//     The first column is all ones; the following columns are powers of x
//     from order `1` to `order`.
//   yMean: Arithmetic mean of y.
//   yStddev: Standard deviation of y.
//   Normalized powers of y: an Tensor2D of shape [batchSize, 1].
function toNormalizedTensors(xyData, order) {
  const batchSize = xyData.length;
  const xData = xyData.map(xy => xy[0]);
  const yData = xyData.map(xy => xy[1]);
  const yMean = mean(yData);
  const yStddev = stddev(yData);
  const yNormalized = normalizeVector(yData, yMean, yStddev);
  const normalizedXPowers = [];
  const xPowerMeans = [];
  const xPowerStddevs = [];
  for (let i = 0; i < order; ++i) {
    const xPower = xData.map(x => Math.pow(x, i + 1));
    const xPowerMean = mean(xPower);
    xPowerMeans.push(xPowerMean);
    const xPowerStddev = stddev(xPower);
    xPowerStddevs.push(xPowerStddev);
    const normalizedXPower = normalizeVector(xPower, xPowerMean, xPowerStddev);
    normalizedXPowers.push(normalizedXPower);
  }
  const xArrayData = [];
  for (let i = 0; i < xData.length; ++i) {
    for (let j = 0; j < order + 1; ++j) {
      if (j === 0) {
        xArrayData.push(1);
      } else {
        xArrayData.push(normalizedXPowers[j - 1][i]);
      }
    }
  }
  return [
    xPowerMeans, xPowerStddevs, tf.tensor2d(xArrayData, [batchSize, order + 1]),
    yMean, yStddev, tf.tensor2d(yNormalized, [batchSize, 1])
  ];
}

// Fit a model for polynomial regression.
//
// Args:
//   xyData: An Array of [x, y] Number Arrays.
//   epochs: How many epochs to train for.
//   learningRate: Learning rate.
//
// Returns: An Array consiting of the following:
//   The trained keras Model instance.
//   xPowerMeans: Arithmetic means of the powers of x, from order `1` to
//      order `order`
//   xPowerStddevs: Standard deviations of the powers of x.
//   yMean: Arithmetic mean of y.
//   yStddev: Standard deviation of y.
async function fitModel(xyData, epochs, learningRate) {
  const batchSize = xyData.length;
  const outputs = toNormalizedTensors(xyData, order);
  const xPowerMeans = outputs[0];
  const xPowerStddevs = outputs[1];
  const xData = outputs[2];
  const yMean = outputs[3];
  const yStddev = outputs[4];
  const yData = outputs[5];
  const input = tf.input({shape: [order + 1]});
  const linearLayer =
      tf.layers.dense({units: 1, kernelInitializer: 'Zeros', useBias: false});
  const output = linearLayer.apply(input);
  const model = tf.model({inputs: input, outputs: output});
  const sgd = tf.train.sgd(learningRate);
  model.compile({optimizer: sgd, loss: 'meanSquaredError'});
  await model.fit(xData, yData, {
    batchSize: batchSize,
    epochs: epochs,
  });
  console.log(
      'Model weights (normalized):',
      model.trainableWeights[0].read().dataSync());
  return [model, xPowerMeans, xPowerStddevs, yMean, yStddev];
}

// Render the predictions made by the model.
function renderModelPredictions(
    canvas, order, model, xPowerMeans, xPowerStddevs, yMean, yStddev) {
  const ctx = canvas.getContext('2d');
  const width = canvas.width;
  let x = -0.5 * width;
  const xStep = 0.02 * width;
  const xs = [];
  const xPowers = [];
  let n = 0;
  while (x < 0.5 * width) {
    xs.push(x);
    let d = 1;
    for (let j = 0; j < order + 1; ++j) {
      xPowers.push(
          j === 0 ? d : ((d - xPowerMeans[j - 1]) / xPowerStddevs[j - 1]));
      d *= x;
    }
    x += xStep;
    n++;
  }

  const predictOut = model.predict(tf.tensor2d(xPowers, [n, order + 1]));
  const normalizedYs = predictOut.dataSync();
  ctx.beginPath();
  let canvasXY = world2canvas(canvas, xs[0], normalizedYs[0] * yStddev + yMean);
  ctx.moveTo(canvasXY[0], canvasXY[1]);
  for (let i = 1; i < n; ++i) {
    canvasXY = world2canvas(canvas, xs[i], normalizedYs[i] * yStddev + yMean);
    ctx.lineTo(canvasXY[0], canvasXY[1]);
    ctx.stroke();
  }
}

// Generate x-y data based on the size of the canvas.
function generateXYData(canvas, coeffs) {
  const data = [];
  for (let x = -canvas.width / 2; x < canvas.width / 2;
       x += canvas.width / 25) {
    data.push([
      x, coeffs[0] * x * x * x + coeffs[1] * x * x + coeffs[2] * x + coeffs[3]
    ]);
  }
  return data;
}

// Fit a model and render the data and predictions.
async function fitAndRender() {
  const epochs = +epochsElement.value;
  const learningRate = +learningRateElement.value;
  if (!isFinite(epochs) || !isFinite(learningRate)) {
    return;
  }
  const coeffs = [
    +cubicCoeffElement.value, +quadCoeffElement.value,
    +linearCoeffElement.value, +constCoeffElement.value
  ];
  console.log('True coefficients: ' + JSON.stringify(coeffs));
  let xyData = generateXYData(canvas, coeffs);
  drawXYData(canvas, xyData);
  const fitOutputs = await fitModel(xyData, epochs, learningRate);
  const model = fitOutputs[0];
  const xPowerMeans = fitOutputs[1];
  const xPowerStddevs = fitOutputs[2];
  const yMean = fitOutputs[3];
  const yStddev = fitOutputs[4];
  await renderModelPredictions(
      canvas, order, model, xPowerMeans, xPowerStddevs, yMean, yStddev);
}

const cubicCoeffElement = document.getElementById('cubic-coeff');
const quadCoeffElement = document.getElementById('quad-coeff');
const linearCoeffElement = document.getElementById('linear-coeff');
const constCoeffElement = document.getElementById('const-coeff');

const epochsElement = document.getElementById('epochs');
const learningRateElement = document.getElementById('learning-rate');

cubicCoeffElement.addEventListener('keyup', fitAndRender);
quadCoeffElement.addEventListener('keyup', fitAndRender);
linearCoeffElement.addEventListener('keyup', fitAndRender);
constCoeffElement.addEventListener('keyup', fitAndRender);

epochsElement.addEventListener('keyup', fitAndRender);
learningRateElement.addEventListener('keyup', fitAndRender);

fitAndRender();
<|endoftext|>


<commit_before>var request = require("request");
var WebSocket = require("ws");

function PushbulletPlugin(bot) {
	var self = this;
	self.name = "pushbullet";
	self.help = "Pushbullet plugin";
	self.depend = ["cmd", "auth", "nickserv", "bitly"];

	self.token = null;
	self.emails = {};
	self.ws = null;
	self.wsTimeout = null;
	self.lastTs = null;

	// https://stackoverflow.com/questions/9907419/javascript-object-get-key-by-value
	self.getKeyByValue = function(obj, value) {
		for (var prop in obj) {
			if(obj.hasOwnProperty(prop)) {
				 if(obj[prop] === value)
					 return prop;
			}
		}
		return null;
	};

	self.setToken = function(token) {
		bot.out.doing("pushbullet", "WS connecting...");
		self.token = token;
		self.ws = new WebSocket("wss://stream.pushbullet.com/websocket/" + self.token);
		self.ws.on("open", function() {
			bot.out.ok("pushbullet", "WS connected");
		});
		self.ws.on("error", function(code, message) {
			bot.out.error("pushbullet", "WS errored (" + code + "): " + message);

			if (self.wsTimeout) {
				clearTimeout(self.wsTimeout);
				self.wsTimeout = null;
			}

			setTimeout(function() { self.setToken(token); }, 30 * 1000);
		});
		self.ws.on("close", function(code, message) {
			bot.out.error("pushbullet", "WS closed (" + code + "): " + message);

			if (self.wsTimeout) {
				clearTimeout(self.wsTimeout);
				self.wsTimeout = null;
			}

			if (code != 1000)
				setTimeout(function() { self.setToken(token); }, 30 * 1000);
		});
		self.ws.on("message", function(message) {
			var data = JSON.parse(message);
			if (data.type == "nop") {
				if (self.wsTimeout) {
					clearTimeout(self.wsTimeout);
					self.wsTimeout = null;
				}

				self.wsTimeout = setTimeout(function() {
					bot.out.warn("pushbullet", "WS missed nop");
					self.setToken(self.token); // reconnect stream
				}, 60 * 1000);
			}
			else if (data.type == "tickle" && data.subtype == "push") {
				self.getPushes(self.lastTs, function(pushes) {
					if (pushes[0])
						self.lastTs = pushes[0].modified;

					pushes.forEach(function(push) {
						if (push.receiver_email == "sim642bot@gmail.com")
							bot.emit("pushbullet#push", push, true);
						else if (push.channel_iden)
							bot.emit("pushbullet#subscription", push, true);
					});
				});
			}
		});
	}

	self.load = function(data) {
		self.setToken(data.token);
		if (data.emails)
			self.emails = data.emails;
		if (data.lastTs)
			self.lastTs = data.lastTs;
	};

	self.save = function() {
		return {
			"token": self.token,
			"emails": self.emails,
			"lastTs": self.lastTs
		};
	};

	self.enable = function() {
		if (self.lastTs === null)
			self.lastTs = Date.now() / 1000;

		self.getPushes(self.lastTs, function(pushes) {
			if (pushes[0])
				self.lastTs = pushes[0].modified;

			pushes.forEach(function(push) {
				if (push.receiver_email == "sim642bot@gmail.com")
					bot.emit("pushbullet#push", push, false);
				else if (push.channel_iden)
					bot.emit("pushbullet#subscription", push, false);
			});
		});
	};

	self.disable = function() {
		self.ws.close(1000, "disabling plugin");
	};

	self.parseto = function(to) {
		if (to in self.emails)
			return self.emails[to];
		else
			return to;
	};

	self.getPushes = function(lastTs, callback) {
		request({
			url: "https://api.pushbullet.com/v2/pushes",
			auth: {
				user: self.token
			},
			json: true,
			qs: {
				"modified_after": lastTs
			}
		}, function (err, res, body) {
			if (!err && res.statusCode == 200) {
				(callback || function(){})(body.pushes);
			}
			else {
				bot.out.error("pushbullet", err);
				bot.out.error("pushbullet", body);
				(callback || function(){})(undefined);
			}
		});
	};

	self.push = function(params, callback) {
		request.post({
			url: "https://api.pushbullet.com/v2/pushes",
			auth: {
				user: self.token
			},
			json: true,
			body: params
		}, function (err, res, body) {
			if (!err && res.statusCode == 200) {
				(callback || function(){})(false);
			}
			else {
				bot.out.error("pushbullet", params);
				bot.out.error("pushbullet", err);
				bot.out.error("pushbullet", body);
				setTimeout(function(){ self.push(params, callback); }, 30 * 1000); // retry pushing again in 30 seconds
			}
		});
	};

	self.pushnote = function(to, title, body, callback) {
		self.push({
			"email": self.parseto(to),
			"type": "note",
			"title": title,
			"body": body
		}, callback);
	};

	self.subscribe = function(channel, callback) {
		request.post({
			url: "https://api.pushbullet.com/v2/subscriptions",
			auth: {
				user: self.token
			},
			json: true,
			body: {
				"channel_tag": channel
			}
		}, function(err, res, body) {
			if (!err && (res.statusCode == 200 || (body.error !== undefined && body.error.type == "invalid_request" && body.error.param == "channel_tag" && body.error.message == "Already subscribed to this channel."))) {
				(callback || function(){})(true, body);
			}
			else {
				bot.out.error("pushbullet", err);
				bot.out.error("pushbullet", body);
				(callback || function(){})(false, body);
			}

		});
	};

	self.events = {
		"pushbullet#push": function(push, live) {
			bot.out.log("pushbullet", push.type + " push" + (live ? "" : "(" + (new Date(push.modified * 1000)).toUTCString() + ")") + " from " + push.sender_email + ": " + push.title);
			bot.emit("pushbullet#push#" + push.sender_email, push, live);

			if (push.title && (push.title in bot.chans)) {
				var sender = self.getKeyByValue(self.emails, push.sender_email);
				if (!sender)
					sender = push.sender_email;
				var msg = "push" + (live ? "" : "ed (" + (new Date(push.modified * 1000)).toUTCString() + ")") + " [\x02" + sender + "\x02] ";

				var text = push.body !== undefined ? push.body.replace(/\n/g, " \\ ") : "";

				switch (push.type) {
				case "note":
					msg += text;
					break;
				case "link":
					msg += (text != "" ? text + " - " : "");
					break;
				case "file":
					msg += (text != "" ? text + " - " : "") + "\x02" + push.file_name + "\x02 (" + push.file_type + "): ";
					break;
				default:
					break;
				}

				if (push.type == "link") {
					if (push.url.length > 50) {
						bot.plugins.bitly.shorten(push.url, function(shortened) {
							msg += shortened;
							bot.say(push.title, msg);
						});
					}
					else {
						msg += push.url;
						bot.say(push.title, msg);
					}
				}
				else if (push.type == "file") {
					bot.plugins.bitly.shorten(push.file_url, function(shortened) {
						msg += shortened;
						bot.say(push.title, msg);
					});
				}
				else {
					bot.say(push.title, msg);
				}
			}
		},

		"pushbullet#subscription": function(push, live) {
			bot.out.log("pushbullet", push.type + " subscription" + (live ? "" : " (" + (new Date(push.modified * 1000)).toUTCString() + ")") + " from " + push.sender_name + ": " + push.title);
			bot.emit("pushbullet#subscription#" + push.sender_name, push, live);
		},

		"cmd#pushbullet": bot.plugins.auth.proxy(6, function(nick, to, args) {
			self.pushnote(args[1], args[2], args[3], function(err) {
				if (err)
					bot.notice(nick, "error sending pushbullet");
				else
					bot.notice(nick, "pushbullet sent to " + args[1]);
			});
		}),

		"cmd#setpushbullet": function(nick, to, args) {
			bot.plugins.nickserv.nickIdentified(nick, function(identified) {
				if (identified) {
					if (args[1] !== undefined) {
						self.emails[nick.toLowerCase()] = args[1];
						bot.notice(nick, "pushbullet set to " + args[1]);
					}
					else {
						delete self.emails[nick.toLowerCase()];
						bot.notice(nick, "pushbullet unset");
					}
				}
				else
					bot.notice(nick, "must be identified for this nick to set pushbullet");
			});
		}
	}
}

module.exports = PushbulletPlugin;
<commit_msg>pushbullet: also store bot email in data<commit_after>var request = require("request");
var WebSocket = require("ws");

function PushbulletPlugin(bot) {
	var self = this;
	self.name = "pushbullet";
	self.help = "Pushbullet plugin";
	self.depend = ["cmd", "auth", "nickserv", "bitly"];

	self.email = null;
	self.token = null;
	self.emails = {};
	self.ws = null;
	self.wsTimeout = null;
	self.lastTs = null;

	// https://stackoverflow.com/questions/9907419/javascript-object-get-key-by-value
	self.getKeyByValue = function(obj, value) {
		for (var prop in obj) {
			if(obj.hasOwnProperty(prop)) {
				 if(obj[prop] === value)
					 return prop;
			}
		}
		return null;
	};

	self.setToken = function(token) {
		bot.out.doing("pushbullet", "WS connecting...");
		self.token = token;
		self.ws = new WebSocket("wss://stream.pushbullet.com/websocket/" + self.token);
		self.ws.on("open", function() {
			bot.out.ok("pushbullet", "WS connected");
		});
		self.ws.on("error", function(code, message) {
			bot.out.error("pushbullet", "WS errored (" + code + "): " + message);

			if (self.wsTimeout) {
				clearTimeout(self.wsTimeout);
				self.wsTimeout = null;
			}

			setTimeout(function() { self.setToken(token); }, 30 * 1000);
		});
		self.ws.on("close", function(code, message) {
			bot.out.error("pushbullet", "WS closed (" + code + "): " + message);

			if (self.wsTimeout) {
				clearTimeout(self.wsTimeout);
				self.wsTimeout = null;
			}

			if (code != 1000)
				setTimeout(function() { self.setToken(token); }, 30 * 1000);
		});
		self.ws.on("message", function(message) {
			var data = JSON.parse(message);
			if (data.type == "nop") {
				if (self.wsTimeout) {
					clearTimeout(self.wsTimeout);
					self.wsTimeout = null;
				}

				self.wsTimeout = setTimeout(function() {
					bot.out.warn("pushbullet", "WS missed nop");
					self.setToken(self.token); // reconnect stream
				}, 60 * 1000);
			}
			else if (data.type == "tickle" && data.subtype == "push") {
				self.getPushes(self.lastTs, function(pushes) {
					if (pushes[0])
						self.lastTs = pushes[0].modified;

					pushes.forEach(function(push) {
						if (push.receiver_email == self.email)
							bot.emit("pushbullet#push", push, true);
						else if (push.channel_iden)
							bot.emit("pushbullet#subscription", push, true);
					});
				});
			}
		});
	}

	self.load = function(data) {
		self.email = data.email;
		self.setToken(data.token);
		if (data.emails)
			self.emails = data.emails;
		if (data.lastTs)
			self.lastTs = data.lastTs;
	};

	self.save = function() {
		return {
			"email": self.email,
			"token": self.token,
			"emails": self.emails,
			"lastTs": self.lastTs
		};
	};

	self.enable = function() {
		if (self.lastTs === null)
			self.lastTs = Date.now() / 1000;

		self.getPushes(self.lastTs, function(pushes) {
			if (pushes[0])
				self.lastTs = pushes[0].modified;

			pushes.forEach(function(push) {
				if (push.receiver_email == self.email)
					bot.emit("pushbullet#push", push, false);
				else if (push.channel_iden)
					bot.emit("pushbullet#subscription", push, false);
			});
		});
	};

	self.disable = function() {
		self.ws.close(1000, "disabling plugin");
	};

	self.parseto = function(to) {
		if (to in self.emails)
			return self.emails[to];
		else
			return to;
	};

	self.getPushes = function(lastTs, callback) {
		request({
			url: "https://api.pushbullet.com/v2/pushes",
			auth: {
				user: self.token
			},
			json: true,
			qs: {
				"modified_after": lastTs
			}
		}, function (err, res, body) {
			if (!err && res.statusCode == 200) {
				(callback || function(){})(body.pushes);
			}
			else {
				bot.out.error("pushbullet", err);
				bot.out.error("pushbullet", body);
				(callback || function(){})(undefined);
			}
		});
	};

	self.push = function(params, callback) {
		request.post({
			url: "https://api.pushbullet.com/v2/pushes",
			auth: {
				user: self.token
			},
			json: true,
			body: params
		}, function (err, res, body) {
			if (!err && res.statusCode == 200) {
				(callback || function(){})(false);
			}
			else {
				bot.out.error("pushbullet", params);
				bot.out.error("pushbullet", err);
				bot.out.error("pushbullet", body);
				setTimeout(function(){ self.push(params, callback); }, 30 * 1000); // retry pushing again in 30 seconds
			}
		});
	};

	self.pushnote = function(to, title, body, callback) {
		self.push({
			"email": self.parseto(to),
			"type": "note",
			"title": title,
			"body": body
		}, callback);
	};

	self.subscribe = function(channel, callback) {
		request.post({
			url: "https://api.pushbullet.com/v2/subscriptions",
			auth: {
				user: self.token
			},
			json: true,
			body: {
				"channel_tag": channel
			}
		}, function(err, res, body) {
			if (!err && (res.statusCode == 200 || (body.error !== undefined && body.error.type == "invalid_request" && body.error.param == "channel_tag" && body.error.message == "Already subscribed to this channel."))) {
				(callback || function(){})(true, body);
			}
			else {
				bot.out.error("pushbullet", err);
				bot.out.error("pushbullet", body);
				(callback || function(){})(false, body);
			}

		});
	};

	self.events = {
		"pushbullet#push": function(push, live) {
			bot.out.log("pushbullet", push.type + " push" + (live ? "" : "(" + (new Date(push.modified * 1000)).toUTCString() + ")") + " from " + push.sender_email + ": " + push.title);
			bot.emit("pushbullet#push#" + push.sender_email, push, live);

			if (push.title && (push.title in bot.chans)) {
				var sender = self.getKeyByValue(self.emails, push.sender_email);
				if (!sender)
					sender = push.sender_email;
				var msg = "push" + (live ? "" : "ed (" + (new Date(push.modified * 1000)).toUTCString() + ")") + " [\x02" + sender + "\x02] ";

				var text = push.body !== undefined ? push.body.replace(/\n/g, " \\ ") : "";

				switch (push.type) {
				case "note":
					msg += text;
					break;
				case "link":
					msg += (text != "" ? text + " - " : "");
					break;
				case "file":
					msg += (text != "" ? text + " - " : "") + "\x02" + push.file_name + "\x02 (" + push.file_type + "): ";
					break;
				default:
					break;
				}

				if (push.type == "link") {
					if (push.url.length > 50) {
						bot.plugins.bitly.shorten(push.url, function(shortened) {
							msg += shortened;
							bot.say(push.title, msg);
						});
					}
					else {
						msg += push.url;
						bot.say(push.title, msg);
					}
				}
				else if (push.type == "file") {
					bot.plugins.bitly.shorten(push.file_url, function(shortened) {
						msg += shortened;
						bot.say(push.title, msg);
					});
				}
				else {
					bot.say(push.title, msg);
				}
			}
		},

		"pushbullet#subscription": function(push, live) {
			bot.out.log("pushbullet", push.type + " subscription" + (live ? "" : " (" + (new Date(push.modified * 1000)).toUTCString() + ")") + " from " + push.sender_name + ": " + push.title);
			bot.emit("pushbullet#subscription#" + push.sender_name, push, live);
		},

		"cmd#pushbullet": bot.plugins.auth.proxy(6, function(nick, to, args) {
			self.pushnote(args[1], args[2], args[3], function(err) {
				if (err)
					bot.notice(nick, "error sending pushbullet");
				else
					bot.notice(nick, "pushbullet sent to " + args[1]);
			});
		}),

		"cmd#setpushbullet": function(nick, to, args) {
			bot.plugins.nickserv.nickIdentified(nick, function(identified) {
				if (identified) {
					if (args[1] !== undefined) {
						self.emails[nick.toLowerCase()] = args[1];
						bot.notice(nick, "pushbullet set to " + args[1]);
					}
					else {
						delete self.emails[nick.toLowerCase()];
						bot.notice(nick, "pushbullet unset");
					}
				}
				else
					bot.notice(nick, "must be identified for this nick to set pushbullet");
			});
		}
	}
}

module.exports = PushbulletPlugin;
<|endoftext|>


<commit_before>var jacksStatus;
var jacksButtons;

var jacksSliceAms;
var jacksAllAms;
var jacksSliceId;

// These variables will be used to communicate with Jacks
var jacksInput;
var jacksOutput;

var jacksMap;
var jacksAMs = {};

// FIXME: A temporary global as the code is transitioned to an
// object-oriented model.
var theJacksApp = null;

// Variable that determines how long (in ms) to wait in between status calls.
var jacksTimeToWait = 5000;

function JacksApp(jacks, status, buttons, sliceAms, allAms, sliceId,
                  readyCallback) {
    // Map from client_id to am_id
    this.client2am = {};
    this.selectedElement = null;
    this.input = null;
    this.output = null;
    this.jacksInput = null;
    this.jacksOutput = null;

    this.jacks = jacks;
    this.status = status;
    this.buttons = buttons;
    this.sliceAms = sliceAms;
    this.allAms = allAms;
    this.sliceId = sliceId;

    // Init globals
    // FIXME: these should all go away
    jacksStatus = status;
    jacksButtons = buttons;
    jacksSliceAms = sliceAms;
    jacksAllAms = allAms;
    jacksSliceId = sliceId;

    var that = this;
    var jacksInstance = new window.Jacks({
        mode: 'viewer',
        source: 'rspec',
        // This may not need to be hardcoded.
        size: { x: 791, y: 350},
        show: {
            menu: false,
            rspec: false,
            version: false
        },
        nodeSelect: false,
        root: jacks,
        readyCallback: function (input, output) {
            that.jacksReady(input, output);
            that.initButtons(jacksButtons);

            // FIXME: these are globals but eventually shouldn't be
            // Commands going into Jacks.
            jacksInput = input;
            // Responses coming out of Jacks.
            jacksOutput = output;

            // Finally, tell our client that we're ready
            readyCallback(that, that.input, that.output);
        }
    });
}

//----------------------------------------------------------------------
// Jacks App Constants
//----------------------------------------------------------------------

JacksApp.prototype.ADD_EVENT_TYPE = "ADD";
JacksApp.prototype.DELETE_EVENT_TYPE = "DELETE";
JacksApp.prototype.MANIFEST_EVENT_TYPE = "MANIFEST";
JacksApp.prototype.RENEW_EVENT_TYPE = "RENEW";
JacksApp.prototype.STATUS_EVENT_TYPE = "STATUS";

//----------------------------------------------------------------------
// Jacks App Methods
//----------------------------------------------------------------------

/**
 * Called when Jacks is ready. 'input' and 'output' are the Jacks
 * input and output event channels.
 */
JacksApp.prototype.jacksReady = function(input, output) {
    // Once Jacks is ready, we can initialize the
    // JacksApp event channels because Backbone has
    // been loaded.
    this.initEvents();

    // Commands going into Jacks.
    this.jacksInput = input;
    // Responses coming out of Jacks.
    this.jacksOutput = output;

    // Set up the function that Jacks will call when a node
    // is clicked.
    this.jacksOutput.on('click-event', this.onClickEvent, this);

    // Start with a blank topology.
    this.jacksInput.trigger('change-topology',
                            [{ rspec: '<rspec></rspec>' }]);

    // Start loading the manifests next, asynchronously.
    var that = this;
    setTimeout(function() {
        that.getSliceManifests();
    }, 0);
}

JacksApp.prototype.initEvents = function() {
    // Initialize input and output as Backbone.Events
    // See http://backbonejs.org
    this.input = new Object();
    this.output = new Object();
    _.extend(this.input, Backbone.Events);
    _.extend(this.output, Backbone.Events);

    // Debug the event channels
    this.input.on("all", function(eventName) {
        console.log("EP -> JacksApp: " + eventName + " event");
    });
    this.input.on(this.MANIFEST_EVENT_TYPE, this.onEpManifest, this);
    this.input.on(this.STATUS_EVENT_TYPE, this.onEpStatus, this);
    this.input.on(this.DELETE_EVENT_TYPE, this.onEpDelete, this);
}

JacksApp.prototype.updateStatus = function(statusText) {
    // FIXME: temporary until globals go away
    var statusPane = this.statusPane || jacksStatus;
    var html = '
' + statusText + '
';
    $(statusPane).prepend(html);
}

JacksApp.prototype.initButtons = function(buttonSelector) {
    var btn = $('<button type="button">Get Manifest</button>');
    var that = this;
    btn.click(function(){ that.getSliceManifests();});
    $(buttonSelector).append(btn);

    var btn = $('<button type="button">Renew</button>');
    btn.click(function() {
        that.renewResources();
    });
    $(buttonSelector).append(btn);

    var dp = $('<input type="text" id="renew_datepicker">');
    $(buttonSelector).append(dp);
    $("#renew_datepicker").datepicker({ dateFormat: "yy-mm-dd" });
    $("#renew_datepicker").attr('placeholder', 'Renew Date');

    btn = $('<button type="button">Delete</button>');
    btn.click(function(){
	    that.deleteResources();
	});
    $(buttonSelector).append(btn);

    btn = $('<button type="button">SSH</button>');
    btn.click(function(){ alert('SSH');});
    $(buttonSelector).append(btn);

    btn = $('<button type="button">Add</button>');
    btn.click(function(){ that.addResources();});
    $(buttonSelector).append(btn);
}

/**
 * Determine whether the status is in a terminal state.
 *
 * Status can be terminal if it is 'ready' or 'failed'. Other states
 * are considered transient, not terminal.
 *
 * Returns a boolean, true if terminal status, false otherwise.
 */
JacksApp.prototype.isTerminalStatus = function(status) {
    var code = status['status_code'];
    /* Which is which? What is 2 and what is 3? */
    return code == 2 || code == 3;
}

//----------------------------------------------------------------------
// Jacks App Events to Embedding Page
//----------------------------------------------------------------------

JacksApp.prototype.getSliceManifests = function() {
    // FIXME: temporary until globals go away
    var sliceAms = this.sliceAms || jacksSliceAms;

    // Loop through each known AM and get the manifest.
    var that = this;
    $.each(sliceAms, function(i, am_id) {
        // Update the status bar.
        that.updateStatus('Gathering manifest from ' + am_id + '...');
        that.output.trigger(that.MANIFEST_EVENT_TYPE,
                            { name: that.MANIFEST_EVENT_TYPE,
                              am_id: am_id,
                              slice_id: jacksSliceId,
                              callback: that.input,
                              client_data: {}
                            });
    });
}

/**
 * max_time is when to stop polling
 */
JacksApp.prototype.getStatus = function(am_id, maxTime) {
    this.updateStatus('Polling resource status from ' + am_id + '...');
    this.output.trigger(this.STATUS_EVENT_TYPE,
                        { name: this.STATUS_EVENT_TYPE,
                          am_id: am_id,
                          slice_id: this.sliceId,
                          callback: this.input,
                          client_data: { maxTime: maxTime }
                        });
}

/**
 * delete all resources on given slice at given AM
 */
JacksApp.prototype.deleteResources = function() {
    // Is anything selected? If so , only delete at that aggregate
    var am_id = this.client2am[this.selectedElement];
    var deleteAMs = this.sliceAms;
    if (am_id) {
        deleteAMs = [am_id];
        var msg = "Delete resources at " + am_id + "?";
    } else {
        var msg = "Delete all slice resources" + "?";
    }
    if (confirm(msg)) {
        var that = this;
        $.each(deleteAMs, function(i, am_id) {
            that.updateStatus('Deleting resources at ' + am_id);
            that.output.trigger(that.DELETE_EVENT_TYPE,
                                { name: that.DELETE_EVENT_TYPE,
                                  am_id: am_id,
                                  slice_id: that.sliceId,
                                  callback: that.input,
                                  client_data: {}
                                })
        });
    }
}

/**
 * Ask embedding page to add resources to current slice
 */
JacksApp.prototype.addResources = function() {
    var that = this;
    that.output.trigger(that.ADD_EVENT_TYPE,
                            { name: that.ADD_EVENT_TYPE,
                              slice_id: jacksSliceId,
                              client_data: {}
                            });

}

JacksApp.prototype.renewResources = function() {
    // Has a date been chosen? If not, help them choose a date
    var renewDate = $('#renew_datepicker').val();
    if (! renewDate) {
        alert("Please choose a renewal date.");
        return;
    }

    // Is anything selected? If so , only renew at that aggregate
    var am_id = this.client2am[this.selectedElement];
    var renewAMs = this.sliceAms;
    if (am_id) {
        renewAMs = [am_id];
        var msg = "Renew resources at " + am_id + " until " + renewDate + "?";
    } else {
        var msg = "Renew all slice resources until " + renewDate + "?";
    }
    if (confirm(msg)) {
        var that = this;
        $.each(renewAMs, function(i, am_id) {
            that.updateStatus('Renewing resources at ' + am_id);
            that.output.trigger(that.RENEW_EVENT_TYPE,
                                { name: that.RENEW_EVENT_TYPE,
                                  am_id: am_id,
                                  slice_id: that.sliceId,
                                  callback: that.input,
                                  client_data: {}
                                })
        });
    }
}

//----------------------------------------------------------------------
// Jacks App Events from Jacks
//----------------------------------------------------------------------

JacksApp.prototype.onClickEvent = function(event) {
    // Jacks currently doens't allow multiple selection for outgoing
    // selections. Once Jacks supports this, the following code will need
    // to handle displaying information for multiple items.

    this.selectedElement = event.client_id;

    $('.jacks #active').attr('id','');
    $('.jacks #'+event['type']+'-'+event['client_id']).parent().attr('id',
                                                                     'active');
    console.log('Event ' + event.type + ': ' + event.client_id);
    //$('#jacksApp'+ji+' .expandedI').each(function() { $(this).removeClass('expandedI') });
    //$('#jacksApp'+ji+' #list-'+event['client_id']).parent().addClass('expandedI');
}

//----------------------------------------------------------------------
// Jacks App Events from Embedding Page
//----------------------------------------------------------------------

JacksApp.prototype.onEpManifest = function(event) {
    if (event.code != 0) {
        console.log("Error retrieving manifest: " + event.output);
        return;
    }

    var rspecManifest = event.value;

    // NEEDS TO BE CHANGED
    // change-topology removes the current topology.
    // The trigger will need to be updated once an event is implemented
    // that adds the manifest to the current topology.
    this.jacksInput.trigger('change-topology', [{ rspec: rspecManifest}]);
    //

    // A map from sliver_id to client_id is needed by some aggregates
    // for the page to find the correct node class inside of Jacks.
    // Used to highlight nodes when they are ready.
    var jacksXml = $($.parseXML(rspecManifest));
    jacksMap = {};

    var that = this;
    var am_id = event.am_id;
    jacksXml.find('node').each(function(i, v) {
        jacksMap[$(this).attr('sliver_id')] = $(this).attr('client_id');
        // This is needed because some AMs do return the client_id, so
        // the mapping needs to have both to avoid needing special cases.
        jacksMap[$(this).attr('client_id')] = $(this).attr('client_id');

        that.client2am[$(this).attr('sliver_id')] = am_id;
        // This is needed because some AMs do return the client_id, so
        // the mapping needs to have both to avoid needing special cases.
        that.client2am[$(this).attr('client_id')] = am_id;

        // Dig out login info
        $(this).find('login').each(function(il, vl) {
            var authn = $(this).attr('authentication');
            var hostname = $(this).attr('hostname');
            var port = $(this).attr('port');
            var username = $(this).attr('username');
            console.log(authn + "://" + username + "@" + hostname + ":" + port);
        });
    });

    jacksAMs[jacksAllAms[am_id]['name']] = [];
    var maxPollTime = Date.now() + this.maxStatusPollSeconds * 1000;
    this.getStatus(am_id, maxPollTime);
}

JacksApp.prototype.onEpStatus = function(event) {
    console.log("onEpStatus");
    if (event.code != 0) {
        console.log("Error retrieving status: " + event.output);
        return;
    }

    // re-poll as necessary up to event.client_data.maxPollTime

    var that = this;
    $.each(event.value, function(i, v) {

// SHOULD PROBABLY CHANGE
      // This only looks for READY and FAILED. There may be other cases to look for.
      // Probably shouldn't poll infinitely.
      if (! that.isTerminalStatus(v)) {
          that.updateStatus('Resources on ' + v['am_name'] + ' are '
                            + v['geni_status'] + '. Polling again in '
                            + jacksTimeToWait/1000 + ' seconds.');
          // Poll again in a little while
          setTimeout(function() {
              that.getStatus(event.am_id, event.client_data.maxTime);
          }, jacksTimeToWait);
      } else if (v['geni_status'] == 'ready') {
          that.updateStatus('Resources on '+v['am_name']+' are ready.');
      } else if (v['geni_status'] == 'failed') {
          that.updateStatus('Resources on '+v['am_name']+' have failed.');
      }

// SHOULD PROBABLY CHANGE
        // This section is for coloring the nodes that are ready.
        // At the moment there is no coloring for failed nodes, etc.
        if (v.hasOwnProperty('resources')) {
            $.each(v['resources'], function(ii, vi) {
                if (vi['geni_status'] == 'ready') {

// NEEDS TO CHANGE
                    // The classes that are targeted will likely need
                    // to change once the restriction of unique client
                    // name is changed to unique client name per
                    // aggregate.
                    //
                    // There has also been talk about Jacks supporting
                    // the page telling it what to highlight, which
                    // would make this less hack-ey.
                    $('.jacks #node-'+jacksMap[vi['geni_urn']]).parent().find('.checkbox').attr('id','ready');
                    $('.jacks #link-'+jacksMap[vi['geni_urn']]).parent().find('.checkbox').attr('id','ready');
                    $('.jacks #list-'+jacksMap[vi['geni_urn']]).parent().find('.itemID').addClass('resourcesReady');

                    jacksAMs[v['am_name']].push(jacksMap[vi['geni_urn']]);
                    console.log(jacksAMs[v['am_name']]);
                }
            });
    }
    });
}

JacksApp.prototype.onEpDelete = function(event) {
    console.log("onEpDelete");
    if (event.code != 0) {
        console.log("Error retrieving status: " + event.output);
        return;
    }

    this.getSliceManifests();

}
<commit_msg>Handle empty AM list case for initialization<commit_after>var jacksStatus;
var jacksButtons;

var jacksSliceAms;
var jacksAllAms;
var jacksSliceId;

// These variables will be used to communicate with Jacks
var jacksInput;
var jacksOutput;

var jacksMap;
var jacksAMs = {};

// FIXME: A temporary global as the code is transitioned to an
// object-oriented model.
var theJacksApp = null;

// Variable that determines how long (in ms) to wait in between status calls.
var jacksTimeToWait = 5000;

function JacksApp(jacks, status, buttons, sliceAms, allAms, sliceId,
                  readyCallback) {
    // Map from client_id to am_id
    this.client2am = {};
    this.selectedElement = null;
    this.input = null;
    this.output = null;
    this.jacksInput = null;
    this.jacksOutput = null;

    this.jacks = jacks;
    this.status = status;
    this.buttons = buttons;
    this.sliceAms = sliceAms;
    this.allAms = allAms;
    this.sliceId = sliceId;

    // Init globals
    // FIXME: these should all go away
    jacksStatus = status;
    jacksButtons = buttons;
    jacksSliceAms = sliceAms;
    jacksAllAms = allAms;
    jacksSliceId = sliceId;

    var that = this;
    var jacksInstance = new window.Jacks({
        mode: 'viewer',
        source: 'rspec',
        // This may not need to be hardcoded.
        size: { x: 791, y: 350},
        show: {
            menu: false,
            rspec: false,
            version: false
        },
        nodeSelect: false,
        root: jacks,
        readyCallback: function (input, output) {
            that.jacksReady(input, output);
            that.initButtons(jacksButtons);

            // FIXME: these are globals but eventually shouldn't be
            // Commands going into Jacks.
            jacksInput = input;
            // Responses coming out of Jacks.
            jacksOutput = output;

            // Finally, tell our client that we're ready
            readyCallback(that, that.input, that.output);
        }
    });
}

//----------------------------------------------------------------------
// Jacks App Constants
//----------------------------------------------------------------------

JacksApp.prototype.ADD_EVENT_TYPE = "ADD";
JacksApp.prototype.DELETE_EVENT_TYPE = "DELETE";
JacksApp.prototype.MANIFEST_EVENT_TYPE = "MANIFEST";
JacksApp.prototype.RENEW_EVENT_TYPE = "RENEW";
JacksApp.prototype.STATUS_EVENT_TYPE = "STATUS";

//----------------------------------------------------------------------
// Jacks App Methods
//----------------------------------------------------------------------

/**
 * Called when Jacks is ready. 'input' and 'output' are the Jacks
 * input and output event channels.
 */
JacksApp.prototype.jacksReady = function(input, output) {
    // Once Jacks is ready, we can initialize the
    // JacksApp event channels because Backbone has
    // been loaded.
    this.initEvents();

    // Commands going into Jacks.
    this.jacksInput = input;
    // Responses coming out of Jacks.
    this.jacksOutput = output;

    // Set up the function that Jacks will call when a node
    // is clicked.
    this.jacksOutput.on('click-event', this.onClickEvent, this);

    // Start with a blank topology.
    this.jacksInput.trigger('change-topology',
                            [{ rspec: '<rspec></rspec>' }]);

    // Start loading the manifests next, asynchronously.
    var that = this;
    setTimeout(function() {
        that.getSliceManifests();
    }, 0);
}

JacksApp.prototype.initEvents = function() {
    // Initialize input and output as Backbone.Events
    // See http://backbonejs.org
    this.input = new Object();
    this.output = new Object();
    _.extend(this.input, Backbone.Events);
    _.extend(this.output, Backbone.Events);

    // Debug the event channels
    this.input.on("all", function(eventName) {
        console.log("EP -> JacksApp: " + eventName + " event");
    });
    this.input.on(this.MANIFEST_EVENT_TYPE, this.onEpManifest, this);
    this.input.on(this.STATUS_EVENT_TYPE, this.onEpStatus, this);
    this.input.on(this.DELETE_EVENT_TYPE, this.onEpDelete, this);
}

JacksApp.prototype.updateStatus = function(statusText) {
    // FIXME: temporary until globals go away
    var statusPane = this.statusPane || jacksStatus;
    var html = '
' + statusText + '
';
    $(statusPane).prepend(html);
}

JacksApp.prototype.initButtons = function(buttonSelector) {
    var btn = $('<button type="button">Get Manifest</button>');
    var that = this;
    btn.click(function(){ that.getSliceManifests();});
    $(buttonSelector).append(btn);

    var btn = $('<button type="button">Renew</button>');
    btn.click(function() {
        that.renewResources();
    });
    $(buttonSelector).append(btn);

    var dp = $('<input type="text" id="renew_datepicker">');
    $(buttonSelector).append(dp);
    $("#renew_datepicker").datepicker({ dateFormat: "yy-mm-dd" });
    $("#renew_datepicker").attr('placeholder', 'Renew Date');

    btn = $('<button type="button">Delete</button>');
    btn.click(function(){
	    that.deleteResources();
	});
    $(buttonSelector).append(btn);

    btn = $('<button type="button">SSH</button>');
    btn.click(function(){ alert('SSH');});
    $(buttonSelector).append(btn);

    btn = $('<button type="button">Add</button>');
    btn.click(function(){ that.addResources();});
    $(buttonSelector).append(btn);
}

/**
 * Determine whether the status is in a terminal state.
 *
 * Status can be terminal if it is 'ready' or 'failed'. Other states
 * are considered transient, not terminal.
 *
 * Returns a boolean, true if terminal status, false otherwise.
 */
JacksApp.prototype.isTerminalStatus = function(status) {
    var code = status['status_code'];
    /* Which is which? What is 2 and what is 3? */
    return code == 2 || code == 3;
}

//----------------------------------------------------------------------
// Jacks App Events to Embedding Page
//----------------------------------------------------------------------

JacksApp.prototype.getSliceManifests = function() {
    // FIXME: temporary until globals go away
    var sliceAms = this.sliceAms || jacksSliceAms;

    if(sliceAms.length == 0) {
	this.updateStatus("Jacks initialized: no resources");
	return;
    }

    // Loop through each known AM and get the manifest.
    var that = this;
    $.each(sliceAms, function(i, am_id) {
        // Update the status bar.
        that.updateStatus('Gathering manifest from ' + am_id + '...');
        that.output.trigger(that.MANIFEST_EVENT_TYPE,
                            { name: that.MANIFEST_EVENT_TYPE,
                              am_id: am_id,
                              slice_id: jacksSliceId,
                              callback: that.input,
                              client_data: {}
                            });
    });
}

/**
 * max_time is when to stop polling
 */
JacksApp.prototype.getStatus = function(am_id, maxTime) {
    this.updateStatus('Polling resource status from ' + am_id + '...');
    this.output.trigger(this.STATUS_EVENT_TYPE,
                        { name: this.STATUS_EVENT_TYPE,
                          am_id: am_id,
                          slice_id: this.sliceId,
                          callback: this.input,
                          client_data: { maxTime: maxTime }
                        });
}

/**
 * delete all resources on given slice at given AM
 */
JacksApp.prototype.deleteResources = function() {
    // Is anything selected? If so , only delete at that aggregate
    var am_id = this.client2am[this.selectedElement];
    var deleteAMs = this.sliceAms;
    if (am_id) {
        deleteAMs = [am_id];
        var msg = "Delete resources at " + am_id + "?";
    } else {
        var msg = "Delete all slice resources" + "?";
    }
    if (confirm(msg)) {
        var that = this;
        $.each(deleteAMs, function(i, am_id) {
            that.updateStatus('Deleting resources at ' + am_id);
            that.output.trigger(that.DELETE_EVENT_TYPE,
                                { name: that.DELETE_EVENT_TYPE,
                                  am_id: am_id,
                                  slice_id: that.sliceId,
                                  callback: that.input,
                                  client_data: {}
                                })
        });
    }
}

/**
 * Ask embedding page to add resources to current slice
 */
JacksApp.prototype.addResources = function() {
    var that = this;
    that.output.trigger(that.ADD_EVENT_TYPE,
                            { name: that.ADD_EVENT_TYPE,
                              slice_id: jacksSliceId,
                              client_data: {}
                            });

}

JacksApp.prototype.renewResources = function() {
    // Has a date been chosen? If not, help them choose a date
    var renewDate = $('#renew_datepicker').val();
    if (! renewDate) {
        alert("Please choose a renewal date.");
        return;
    }

    // Is anything selected? If so , only renew at that aggregate
    var am_id = this.client2am[this.selectedElement];
    var renewAMs = this.sliceAms;
    if (am_id) {
        renewAMs = [am_id];
        var msg = "Renew resources at " + am_id + " until " + renewDate + "?";
    } else {
        var msg = "Renew all slice resources until " + renewDate + "?";
    }
    if (confirm(msg)) {
        var that = this;
        $.each(renewAMs, function(i, am_id) {
            that.updateStatus('Renewing resources at ' + am_id);
            that.output.trigger(that.RENEW_EVENT_TYPE,
                                { name: that.RENEW_EVENT_TYPE,
                                  am_id: am_id,
                                  slice_id: that.sliceId,
                                  callback: that.input,
                                  client_data: {}
                                })
        });
    }
}

//----------------------------------------------------------------------
// Jacks App Events from Jacks
//----------------------------------------------------------------------

JacksApp.prototype.onClickEvent = function(event) {
    // Jacks currently doens't allow multiple selection for outgoing
    // selections. Once Jacks supports this, the following code will need
    // to handle displaying information for multiple items.

    this.selectedElement = event.client_id;

    $('.jacks #active').attr('id','');
    $('.jacks #'+event['type']+'-'+event['client_id']).parent().attr('id',
                                                                     'active');
    console.log('Event ' + event.type + ': ' + event.client_id);
    //$('#jacksApp'+ji+' .expandedI').each(function() { $(this).removeClass('expandedI') });
    //$('#jacksApp'+ji+' #list-'+event['client_id']).parent().addClass('expandedI');
}

//----------------------------------------------------------------------
// Jacks App Events from Embedding Page
//----------------------------------------------------------------------

JacksApp.prototype.onEpManifest = function(event) {
    if (event.code != 0) {
        console.log("Error retrieving manifest: " + event.output);
        return;
    }

    var rspecManifest = event.value;

    // NEEDS TO BE CHANGED
    // change-topology removes the current topology.
    // The trigger will need to be updated once an event is implemented
    // that adds the manifest to the current topology.
    this.jacksInput.trigger('change-topology', [{ rspec: rspecManifest}]);
    //

    // A map from sliver_id to client_id is needed by some aggregates
    // for the page to find the correct node class inside of Jacks.
    // Used to highlight nodes when they are ready.
    var jacksXml = $($.parseXML(rspecManifest));
    jacksMap = {};

    var that = this;
    var am_id = event.am_id;
    jacksXml.find('node').each(function(i, v) {
        jacksMap[$(this).attr('sliver_id')] = $(this).attr('client_id');
        // This is needed because some AMs do return the client_id, so
        // the mapping needs to have both to avoid needing special cases.
        jacksMap[$(this).attr('client_id')] = $(this).attr('client_id');

        that.client2am[$(this).attr('sliver_id')] = am_id;
        // This is needed because some AMs do return the client_id, so
        // the mapping needs to have both to avoid needing special cases.
        that.client2am[$(this).attr('client_id')] = am_id;

        // Dig out login info
        $(this).find('login').each(function(il, vl) {
            var authn = $(this).attr('authentication');
            var hostname = $(this).attr('hostname');
            var port = $(this).attr('port');
            var username = $(this).attr('username');
            console.log(authn + "://" + username + "@" + hostname + ":" + port);
        });
    });

    jacksAMs[jacksAllAms[am_id]['name']] = [];
    var maxPollTime = Date.now() + this.maxStatusPollSeconds * 1000;
    this.getStatus(am_id, maxPollTime);
}

JacksApp.prototype.onEpStatus = function(event) {
    console.log("onEpStatus");
    if (event.code != 0) {
        console.log("Error retrieving status: " + event.output);
        return;
    }

    // re-poll as necessary up to event.client_data.maxPollTime

    var that = this;
    $.each(event.value, function(i, v) {

// SHOULD PROBABLY CHANGE
      // This only looks for READY and FAILED. There may be other cases to look for.
      // Probably shouldn't poll infinitely.
      if (! that.isTerminalStatus(v)) {
          that.updateStatus('Resources on ' + v['am_name'] + ' are '
                            + v['geni_status'] + '. Polling again in '
                            + jacksTimeToWait/1000 + ' seconds.');
          // Poll again in a little while
          setTimeout(function() {
              that.getStatus(event.am_id, event.client_data.maxTime);
          }, jacksTimeToWait);
      } else if (v['geni_status'] == 'ready') {
          that.updateStatus('Resources on '+v['am_name']+' are ready.');
      } else if (v['geni_status'] == 'failed') {
          that.updateStatus('Resources on '+v['am_name']+' have failed.');
      }

// SHOULD PROBABLY CHANGE
        // This section is for coloring the nodes that are ready.
        // At the moment there is no coloring for failed nodes, etc.
        if (v.hasOwnProperty('resources')) {
            $.each(v['resources'], function(ii, vi) {
                if (vi['geni_status'] == 'ready') {

// NEEDS TO CHANGE
                    // The classes that are targeted will likely need
                    // to change once the restriction of unique client
                    // name is changed to unique client name per
                    // aggregate.
                    //
                    // There has also been talk about Jacks supporting
                    // the page telling it what to highlight, which
                    // would make this less hack-ey.
                    $('.jacks #node-'+jacksMap[vi['geni_urn']]).parent().find('.checkbox').attr('id','ready');
                    $('.jacks #link-'+jacksMap[vi['geni_urn']]).parent().find('.checkbox').attr('id','ready');
                    $('.jacks #list-'+jacksMap[vi['geni_urn']]).parent().find('.itemID').addClass('resourcesReady');

                    jacksAMs[v['am_name']].push(jacksMap[vi['geni_urn']]);
                    console.log(jacksAMs[v['am_name']]);
                }
            });
    }
    });
}

JacksApp.prototype.onEpDelete = function(event) {
    console.log("onEpDelete");
    if (event.code != 0) {
        console.log("Error retrieving status: " + event.output);
        return;
    }

    this.updateStatus("Resources deleted");

    this.getSliceManifests();

}
<|endoftext|>


<commit_before>import React from 'react';
import Radium from 'radium';
import urlParse from 'url';
import request from 'superagent';
import async from 'async';
import Tabs from 'react-simpletabs';
import moment from 'moment';
import Loader from 'halogen/PulseLoader';
import outlier from 'outlier';
import _ from 'lodash';

let styles = {};

export default class Results extends React.Component {

	constructor(props) {
		super(props);
		this.state = { sources: {}, fetched: false };
	}
	componentDidMount() {
		console.log('mounted!', this.props);

		if (this.props.sources && this.props.sources.length >= 1) {
			const sources = this.props.sources;
			const query = this.props.query;
			this.fetchSources(query, sources);
		}
	}
	componentWillReceiveProps(nextProps) {
		console.log('got new props', nextProps);

		if (nextProps.sources && nextProps.sources.length >= 1) {
			const sources = nextProps.sources;
			const query = nextProps.query;
			this.fetchSources(query, sources);
		}
	}
	fetchSources(query, sources) {
		const sourceFuncs = [];

		for (const source of sources) {
			sourceFuncs.push(this.fetchSource.bind(this, query, source));
		}
		async.series(sourceFuncs);
	}
	parseSource(sourceName, body) {
		return body.map((article) => {
			return { url: article.guid, source: sourceName, title: article.title, date: article.publish_date};
		});
	}
	fetchSource(query, source, callback) {

		const newQ = `${query.q} AND ${source.value}`;
		console.log('fetching!', newQ);
		request
			.post('/stories')
			.query({ q: newQ, fq: query.fq })
			.send({ q: newQ, fq: query.fq })
			.end(function(err, res) {
				console.log('fetched!!', source);
				// const articles = this.state.articles.concat(this.parseSource(source.label, res.body));
				const reqdata = JSON.parse(res.text).data;
				const articles = this.parseSource(source.label, reqdata);
				const sourceObj = this.state.sources;
				sourceObj[source.label] = { articles, fetched: true, name: source.label };
				this.setState({sources: sourceObj, fetched: true});
				this.forceUpdate();
				callback(null, articles);
			}.bind(this));
	}

	submitResults() {
		const story = this.props.name;
		const sourceNames = this.props.sources.map((source) => source.label);

		const flattenedArticles = Object.values(this.state.sources).map((source) => source.articles).reduce(function(a, b) { return a.concat(b); }, []);

		const newscloudURL = (document.location.hostname == "localhost") ? 'http://localhost:8083' :  'http://newsclouds.media.mit.edu';

    request
			.post(`${newscloudURL}/createStory`)
      .send({ story, articles: flattenedArticles, sourceNames})
			.end(function(err, res) {
				console.log('fetched!!');
				console.log(res);
				this.setState({saved: true});
			}.bind(this));
	}

	processOutliers() {
		const flattenedArticles = Object.values(this.state.sources).map((source) => source.articles).reduce(function(a, b) { return a.concat(b); }, []);
		const minDate = _.min(flattenedArticles, 'date').date;
		const dateDistribution = flattenedArticles.map((article) => moment(minDate).diff(moment(article.date),'days'));
		console.log(dateDistribution);

		const outliers = outlier(dateDistribution).findOutliers(); // [51]

		console.log(outliers);

		// dateSpread = moment(dateEnd).diff(moment(momentStart),'days')

	}

	render() {

		if (this.state.fetched) {
			this.processOutliers();
		}
		return (

				{(!this.state.saved) ?

Results
          Create

				{
					(this.state.fetched) ?
					<Tabs key="tab">
						{
							Object.values(this.state.sources).map((sourceObj, index) => {
								const articleSources = sourceObj.articles;

								return (<Tabs.Panel key={sourceObj.name} title={`${sourceObj.name} (${sourceObj.articles.length})`}>
									<div key={`${sourceObj.name}-wrapper`}>
										{articleSources.map((article, articleIndex) => {
											return (

												<span key={`${article.title}button`} style={styles.button}>Use
												{moment(article.date).format("MMM Do YY")}
												{article.title}

);
										})}

				        </Tabs.Panel>);
							})
						}
		      </Tabs>
					:

				 		<Loader color="#666" size="16px" margin="4px"/>

			}

				:

Saved your NewsCloud "{this.props.name}"!

It may take 1-2 minutes for processing of the cloud to complete.

}

		);
	}
}

styles = {
	loader: {
		width: '100%',
		textAlign: 'center',
	},
	link: {
		color: '#222'
	},
	title: {
		color: '#666',
		marginBottom: '75px',
		fontWeight: 300,
		fontSize: '2em'
	},
	createButton: {
		float: 'right',
    fontSize: '0.50em',
		borderColor: '#666',
	},
	button: {
		display: 'inline-block',
		color: '#666',
		border: '2px solid black',
		margin: '0px 5px',
		padding: '3px',
		minWidth: '50px',
		textAlign: 'center',
		cursor: 'pointer',
	},
	overlay: {
		position: 'fixed',
		left: '12%',
		top: 'calc(4em + 5vh)',
		width: '75vw',
		height: '85vh',
		overflow: 'scroll',
		backgroundColor: 'white',
		padding: '20px',
		boxShadow: '0 1px 3px 0 rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12)',
		zIndex: 1000,
	},
	block: {
		marginTop: '20px',
		whiteSpace: 'nowrap',
	}
};

export default Radium(Results);
<commit_msg>https url<commit_after>import React from 'react';
import Radium from 'radium';
import urlParse from 'url';
import request from 'superagent';
import async from 'async';
import Tabs from 'react-simpletabs';
import moment from 'moment';
import Loader from 'halogen/PulseLoader';
import outlier from 'outlier';
import _ from 'lodash';

let styles = {};

export default class Results extends React.Component {

	constructor(props) {
		super(props);
		this.state = { sources: {}, fetched: false };
	}
	componentDidMount() {
		console.log('mounted!', this.props);

		if (this.props.sources && this.props.sources.length >= 1) {
			const sources = this.props.sources;
			const query = this.props.query;
			this.fetchSources(query, sources);
		}
	}
	componentWillReceiveProps(nextProps) {
		console.log('got new props', nextProps);

		if (nextProps.sources && nextProps.sources.length >= 1) {
			const sources = nextProps.sources;
			const query = nextProps.query;
			this.fetchSources(query, sources);
		}
	}
	fetchSources(query, sources) {
		const sourceFuncs = [];

		for (const source of sources) {
			sourceFuncs.push(this.fetchSource.bind(this, query, source));
		}
		async.series(sourceFuncs);
	}
	parseSource(sourceName, body) {
		return body.map((article) => {
			return { url: article.guid, source: sourceName, title: article.title, date: article.publish_date};
		});
	}
	fetchSource(query, source, callback) {

		const newQ = `${query.q} AND ${source.value}`;
		console.log('fetching!', newQ);
		request
			.post('/stories')
			.query({ q: newQ, fq: query.fq })
			.send({ q: newQ, fq: query.fq })
			.end(function(err, res) {
				console.log('fetched!!', source);
				// const articles = this.state.articles.concat(this.parseSource(source.label, res.body));
				const reqdata = JSON.parse(res.text).data;
				const articles = this.parseSource(source.label, reqdata);
				const sourceObj = this.state.sources;
				sourceObj[source.label] = { articles, fetched: true, name: source.label };
				this.setState({sources: sourceObj, fetched: true});
				this.forceUpdate();
				callback(null, articles);
			}.bind(this));
	}

	submitResults() {
		const story = this.props.name;
		const sourceNames = this.props.sources.map((source) => source.label);

		const flattenedArticles = Object.values(this.state.sources).map((source) => source.articles).reduce(function(a, b) { return a.concat(b); }, []);

		const newscloudURL = (document.location.hostname == "localhost") ? 'http://localhost:8083' :  'https://newsclouds.media.mit.edu';

    request
			.post(`${newscloudURL}/createStory`)
      .send({ story, articles: flattenedArticles, sourceNames})
			.end(function(err, res) {
				console.log('fetched!!');
				console.log(res);
				this.setState({saved: true});
			}.bind(this));
	}

	processOutliers() {
		const flattenedArticles = Object.values(this.state.sources).map((source) => source.articles).reduce(function(a, b) { return a.concat(b); }, []);
		const minDate = _.min(flattenedArticles, 'date').date;
		const dateDistribution = flattenedArticles.map((article) => moment(minDate).diff(moment(article.date),'days'));
		console.log(dateDistribution);

		const outliers = outlier(dateDistribution).findOutliers(); // [51]

		console.log(outliers);

		// dateSpread = moment(dateEnd).diff(moment(momentStart),'days')

	}

	render() {

		if (this.state.fetched) {
			this.processOutliers();
		}
		return (

				{(!this.state.saved) ?

Results
          Create

				{
					(this.state.fetched) ?
					<Tabs key="tab">
						{
							Object.values(this.state.sources).map((sourceObj, index) => {
								const articleSources = sourceObj.articles;

								return (<Tabs.Panel key={sourceObj.name} title={`${sourceObj.name} (${sourceObj.articles.length})`}>
									<div key={`${sourceObj.name}-wrapper`}>
										{articleSources.map((article, articleIndex) => {
											return (

												<span key={`${article.title}button`} style={styles.button}>Use
												{moment(article.date).format("MMM Do YY")}
												{article.title}

);
										})}

				        </Tabs.Panel>);
							})
						}
		      </Tabs>
					:

				 		<Loader color="#666" size="16px" margin="4px"/>

			}

				:

Saved your NewsCloud "{this.props.name}"!

It may take 1-2 minutes for processing of the cloud to complete.

}

		);
	}
}

styles = {
	loader: {
		width: '100%',
		textAlign: 'center',
	},
	link: {
		color: '#222'
	},
	title: {
		color: '#666',
		marginBottom: '75px',
		fontWeight: 300,
		fontSize: '2em'
	},
	createButton: {
		float: 'right',
    fontSize: '0.50em',
		borderColor: '#666',
	},
	button: {
		display: 'inline-block',
		color: '#666',
		border: '2px solid black',
		margin: '0px 5px',
		padding: '3px',
		minWidth: '50px',
		textAlign: 'center',
		cursor: 'pointer',
	},
	overlay: {
		position: 'fixed',
		left: '12%',
		top: 'calc(4em + 5vh)',
		width: '75vw',
		height: '85vh',
		overflow: 'scroll',
		backgroundColor: 'white',
		padding: '20px',
		boxShadow: '0 1px 3px 0 rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12)',
		zIndex: 1000,
	},
	block: {
		marginTop: '20px',
		whiteSpace: 'nowrap',
	}
};

export default Radium(Results);
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import GrommetApp from 'grommet/components/App'
import LandingHeader from '../../components/landingheader'
import HeroImage from '../../static/hero.jpg'
import LandingHero from '../../components/landinghero'
import FeatureCard from '../../components/featurecard'
import Tiles from 'grommet/components/Tiles'
import Box from 'grommet/components/Box'
import Free from '../../static/free.jpg'
import LandingFooter from '../../components/landingfooter'
import ChapterAdd from 'grommet/components/icons/base/ChapterAdd'
import Apps from 'grommet/components/icons/base/Apps'
import Gamepad from 'grommet/components/icons/base/Gamepad'
import Book from 'grommet/components/icons/base/Book'
import Star from 'grommet/components/icons/base/Star'
import FeatureShow from '../../components/featureshow'

export class LandingView extends Component {

  render () {
    return (
      <GrommetApp centered={false}>
        <Box>
          <LandingHeader />
          <LandingHero image={HeroImage} grommetColor='grey-1-a' heading='An exam prep platform for Personal Vocabulary Management.' desc='Whether youʼre an English major or appearing for exams with English sections like SAT, GMAT, GRE, LSAT etc or any other Entrance Exam,
            building vocabulary is the first step and the most important.' getStarted='Get Started' cardColor='grey-1' label='Mission Admission' justify='end' />
        </Box>
        <Box pad='medium' colorIndex='grey-1-a'>
          <Tiles size='medium' justify='center' pad='large'>
            <FeatureCard heading='Flashcards' para='Store words as flashcards with meaning, example sentences,
              pronunciation & personal notes!' />
            <FeatureCard heading='Your Words' para='Segregate your words into lists' />
            <FeatureCard heading='Games' para='Play word games' />
            <FeatureCard heading='Starter Words' para='Sample word pack with the most recurring words to get you up and running' />
            <FeatureCard heading='Favourites' para='Store favourites' />
          </Tiles>
        </Box>
        <LandingHero image={Free} grommetColor='light-1' heading='Try it for free today!' getStarted='Get Started' justify='center' />
        <Box pad='large' align="center">
          <Tiles size='medium' justify='center' pad='large'>
            <FeatureShow icon={<ChapterAdd colorIndex='brand' size='large' />} heading='Flashcards' para='Store words as flashcards with meaning, example sentences, pronunciation & personal notes!'/>
            <FeatureShow icon={<Apps colorIndex='brand' size='large' />} heading='Your Words' para='Segregate your words into lists'/>
            <FeatureShow icon={<Gamepad colorIndex='brand' size='large' />} heading='Games' para='Play word games'/>
            <FeatureShow icon={<Book colorIndex='brand' size='large' />} heading='Starter Words' para='Sample word pack with the most recurring words to get you up and running'/>
            <FeatureShow icon={<Star colorIndex='brand' size='large' />} heading='Favourites' para='Store favourites'/>
          </Tiles>
        </Box>
        <LandingFooter />
      </GrommetApp>
    )
  }
}

LandingView.propTypes = {

}

export default LandingView
<commit_msg>header text change<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import GrommetApp from 'grommet/components/App'
import LandingHeader from '../../components/landingheader'
import HeroImage from '../../static/hero.jpg'
import LandingHero from '../../components/landinghero'
import FeatureCard from '../../components/featurecard'
import Tiles from 'grommet/components/Tiles'
import Box from 'grommet/components/Box'
import Free from '../../static/free.jpg'
import LandingFooter from '../../components/landingfooter'
import ChapterAdd from 'grommet/components/icons/base/ChapterAdd'
import Apps from 'grommet/components/icons/base/Apps'
import Gamepad from 'grommet/components/icons/base/Gamepad'
import Book from 'grommet/components/icons/base/Book'
import Star from 'grommet/components/icons/base/Star'
import FeatureShow from '../../components/featureshow'

export class LandingView extends Component {

  render () {
    return (
      <GrommetApp centered={false}>
        <Box>
          <LandingHeader />
          <LandingHero image={HeroImage} grommetColor='grey-1-a' heading='Giving your vocabulary preparation awesome super powers!' desc='Whether youʼre an English major or appearing for exams with English sections like SAT, GMAT, GRE, LSAT etc or any other Entrance Exam,
            building vocabulary is the first step and also the most important.' getStarted='Get Started' cardColor='grey-1' label='Mission Admission' justify='end' />
        </Box>
        <Box pad='medium' colorIndex='grey-1-a'>
          <Tiles size='medium' justify='center' pad='large'>
            <FeatureCard heading='Flashcards' para='Store words as flashcards with meaning, example sentences,
              pronunciation & personal notes!' />
            <FeatureCard heading='Your Words' para='Segregate your words into lists' />
            <FeatureCard heading='Games' para='Play word games' />
            <FeatureCard heading='Starter Words' para='Sample word pack with the most recurring words to get you up and running' />
            <FeatureCard heading='Favourites' para='Store favourites' />
          </Tiles>
        </Box>
        <LandingHero image={Free} grommetColor='light-1' heading='Try it for free today!' getStarted='Get Started' justify='center' />
        <Box pad='large' align="center">
          <Tiles size='medium' justify='center' pad='large'>
            <FeatureShow icon={<ChapterAdd colorIndex='brand' size='large' />} heading='Flashcards' para='Store words as flashcards with meaning, example sentences, pronunciation & personal notes!'/>
            <FeatureShow icon={<Apps colorIndex='brand' size='large' />} heading='Your Words' para='Segregate your words into lists'/>
            <FeatureShow icon={<Gamepad colorIndex='brand' size='large' />} heading='Games' para='Play word games'/>
            <FeatureShow icon={<Book colorIndex='brand' size='large' />} heading='Starter Words' para='Sample word pack with the most recurring words to get you up and running'/>
            <FeatureShow icon={<Star colorIndex='brand' size='large' />} heading='Favourites' para='Store favourites'/>
          </Tiles>
        </Box>
        <LandingFooter />
      </GrommetApp>
    )
  }
}

LandingView.propTypes = {

}

export default LandingView
<|endoftext|>