<commit_before>var optimjs = (function (exports) {

    // export public members
    exports = exports || {};

    exports.minimize_Powell = function (fnc, x0) {
        // fnc: function which takes array of size N as an input
        // x0: array or real numbers of size N;
        // serves as initialization of algorithm.

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at optimum

        // maximum absolute gradient magnitude
        var eps = 1e-2;

        var convergence = false;
        var x = x0.slice(); // make copy of initialization
        var alpha = 0.001; // scaling factor

        var pfx = Math.exp(10);
        var fx = fnc(x);
        var pidx = 1;
        while (!convergence) {

            var indicies = optimjs.shuffleIndiciesOf(x);

            convergence = true;

            // Perform update over all of the variables in random order
            for (var i = 0; i < indicies.length; i++) {

                x[indicies[i]] += 1e-6;
                var fxi = fnc(x);
                x[indicies[i]] -= 1e-6;
                var dx = (fxi - fx) / 1e-6;

                if (Math.abs(dx) > eps) {
                    convergence = false;
                }

                x[indicies[i]] = x[indicies[i]] - alpha * dx;
                fx = fnc(x);

            }

            // a simple step size selection rule. Near x function acts linear
            // (this is assumed at least) and thus very small values of alpha
            // should lead to (small) improvement. Increasing alpha would
            // yield better improvement up to certain alpha size.

            alpha = pfx > fx ? alpha * 1.1 : alpha * 0.7;
            pfx = fx;

            pidx--;
            if (pidx === 0) {
                pidx = 1;
                console.log(fx);
            }

        }

        var solution = {};
        solution.argument = x;
        solution.fncvalue = fx;

        return solution;

    };

    exports.minimize_GradientDescent = function (fnc, grd, x0) {
        // fnc: function which takes array of size N as an input
        // grd: gradient (array of size N) of function for some input
        // x0: array or real numbers of size N;
        // serves as initialization of algorithm.

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at found optimum
        var x = x0.slice();

        var convergence = false;
        var eps = 1e-3;
        var alpha = 0.01;

        var pfx = fnc(x);

        while (!convergence) {
            var g = grd(x);
            convergence = optimjs.vect_max_abs_x_less_eps(g, eps);

            if (convergence) {
                break;
            }

            var repeat = true;

            // a simple step size selection rule. Near x function acts linear
            // (this is assumed at least) and thus very small values of alpha
            // should lead to (small) improvement. Increasing alpha would
            // yield better improvement up to certain alpha size.

            while (repeat) {
                var xn = x.slice();
                optimjs.vect_x_pluseq_ag(xn, -alpha, g); // perform step
                var fx = fnc(xn);

                repeat = pfx < fx;
                // this automatically selects step size
                alpha = repeat ? alpha * 0.7 : alpha * 1.1;
            }

            x = xn;
            pfx = fx;

        }

        var solution = {};
        solution.argument = x;
        solution.fncvalue = fx;
        return solution;

    };

    exports.minimize_L_BFGS = function (fnc, grd, x0) {
        // fnc: function which takes array of size N as an input
        // grd: gradient (array of size N) of function for some input
        // x0: array or real numbers of size N;
        // serves as initialization of algorithm.

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at found optimum
        var x = x0.slice();

        var eps = 1e-3; // max abs value of gradient component for termination
        var alpha = 0.001; // initial step size
        var m = 5; // history size to keep for Hessian approximation

        var pfx = fnc(x);
        var s = []; // this is needed for lbfgs procedure
        var y = [];
        var ro = [];

        var g = grd(x);
        var direction = g.slice();
        var convergence = false;
        while (!convergence) {

            var xn = x.slice();
            optimjs.vect_x_pluseq_ag(xn, alpha, direction); // perform step
            var fx = fnc(xn);
            alpha = pfx < fx ? alpha * 0.5 : alpha * 1.2; // magic!

            //  < ================= apply limited memory BFGS procedure ================= >
            var gn = grd(xn);

            if (optimjs.vect_max_abs_x_less_eps(gn, eps)) {
                break;
            }

            var dx = optimjs.vect_a_minus_b(xn, x);
            var dg = optimjs.vect_a_minus_b(gn, g);

            s.unshift(dx);
            y.unshift(dg);
            var tmp = 1 / (optimjs.dot(dx, dg));
            ro.unshift(tmp);

            if (s.length > m) {
                s.pop();
                y.pop();
                ro.pop();
            }

            var r = g.slice();
            var a = new Array(s.length);

            for (var i = 0; i < s.length; i++) {
                var pi = 1 / (optimjs.dot(s[i], y[i]));
                a[i] = pi * optimjs.dot(s[i], r);
                optimjs.vect_x_pluseq_ag(r, -a[i], y[i]);
            }

            // perform Hessian scaling
            var scale = optimjs.dot(dx, dg) / optimjs.dot(dg, dg);
            for (var i = 0; i < r.length; i++) {
                r[i] = r[i] * scale;
            }

            for (var i = 0; i < s.length; i++) {
                var j = s.length - i - 1;
                var pj = 1 / (optimjs.dot(s[j], y[j]));
                var beta = pj * optimjs.dot(y[j], r);
                optimjs.vect_x_pluseq_ag(r, (a[j] - beta), s[j]);
            }
            direction = r.slice();

            for (var i = 0; i < direction.length; i++) {
                if (isNaN(direction[i])) {
                    convergence = true; // something went wrong. Stop now
                }
            }

            //  < ================= apply limited memory BFGS procedure ================= >

            for (var i = 0; i < direction.length; i++) {
                direction[i] = -direction[i];
            }

            pfx = fx;
            x = xn;
            g = gn;

            console.log("fv = " + fx);

        }

        var solution = {};
        solution.argument = x;
        solution.fncvalue = fx;
        return solution;

    };

    exports.minimize_SGD = function (I, fnc, grd, W0){
        // This routine assumes that objective can be written as
        //          f(W) = \sum_{i \in [n]} g(W)
        // and using this poperty gradient is updated for every separate i, which results in speedups.
        //
        // inputs:
        // I: array of indicies of all of points in the dataset (e.g. 1 ... 1000)
        // fnc: function which takes as input parameter W and index of point with index i
        // grd: gradient over parameter W of fnc for point with index i
        // W0: initial value of parameters of fnc

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at optimum

        var splitp = Math.floor(I.length*0.7); // 70 % is used for training , rest for validation

        var Iv = I.slice(splitp+1);
        var I = I.slice(0, splitp);

        var W = W0.slice();

        var alpha = 0.00001; // initial alpha guess
        var checks = 5; // validation checks until stopping
        var fmin = Math.exp(30); // initial minimum value (something big)
        var maxiternum = 70; // maximum number of iterations

        var Wbest = W.slice();
        var idx = 0;

        while(checks > 0){
            idx ++;
            // perform gradient update
            for (var i = 0; i < I.length; i++) {
                var gr = grd(W,I[i]);
                optimjs.vect_x_pluseq_ag(W, -alpha, gr);
            }

            var floc = 0;

            // perform cross - validation
            for (var i = 0; i < Iv.length; i++) {
                var fv = fnc(W,Iv[i]);
                floc += fv;
            }

            if (floc < fmin) {
                fmin = floc;
                Wbest = W.slice();
                alpha *= 1.1;
                checks = 10;
            }
            else
            {
                checks -= 1;
                alpha *= 0.5;
            }

            // log the current state
            console.log("epoch with fv = " + fmin + " alpha= " + alpha);
            if (idx > maxiternum) {
                break;
            }
        }

        var solution = {};
        solution.argument = Wbest.slice();
        solution.fncvalue = fmin;
        return solution;

    };

    exports.numerical_gradient = function (fnc, x) {
        // can be used as for gradient check or its substitute. Gradient is approx. via forward difference
        var grad = x.slice();
        var fx = fnc(x);
        var h = 1e-6; // step size

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

            // approximation using simple forward difference
            x[i] += h;
            var fxi = fnc(x);
            x[i] -= h;

            grad[i] = (fxi - fx) / h;
        }
        return grad;
    };

    // some vector operations used in all of the optimization procedures above

    exports.shuffleIndiciesOf = function (array) {
        // returns shuffled indicies of arrray
        var idx = [];
        for (var i = 0; i < array.length; i++) {
            idx.push(i);
        }
        for (var i = 0; i < array.length; i++) {
            var j = Math.floor(Math.random() * i);
            var tmp = idx[i];
            idx[i] = idx[j];
            idx[j] = tmp;
        }
        return idx;
    };

    exports.dot = function (a, b) {
        // computes dot product
        var result = 0;
        for (var i = 0; i < a.length; i++) {
            result += a[i] * b[i];
        }
        return result;

    };

    exports.vect_x_times_c = function (x, c) {
        // vector times constant
        var r = x.slice();
        for (var i = 0; i < x.length; i++) {
            r[i]  = r[i] * c;
        }
        return r;

    };

    exports.vect_a_minus_b = function (a, b) {
        // vector difference
        var result = new Array(a.length);
        for (var i = 0; i < a.length; i++) {
            result[i] = a[i] - b[i];
        }
        return result;

    };

    exports.vect_x_pluseq_ag = function (x, a, g) {
        // used for fixed step size updating value of x
        for (var i = 0; i < x.length; i++) {
            x[i] = x[i] + a * g[i];
        }

        return x;

    };

    exports.vect_max_abs_x_less_eps = function (x, eps) {
        // this procedure is used for stopping criterion check
        for (var i = 0; i < x.length; i++) {
            if (Math.abs(x[i]) >= eps) {
                return false;
            }
        }
        return true;
    };

    return exports;

})(typeof module != 'undefined' && module.exports);  // add exports to module.exports if in node.js
<commit_msg>Remove unnecessary console logging<commit_after>var optimjs = (function (exports) {

    // export public members
    exports = exports || {};

    exports.minimize_Powell = function (fnc, x0) {
        // fnc: function which takes array of size N as an input
        // x0: array or real numbers of size N;
        // serves as initialization of algorithm.

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at optimum

        // maximum absolute gradient magnitude
        var eps = 1e-2;

        var convergence = false;
        var x = x0.slice(); // make copy of initialization
        var alpha = 0.001; // scaling factor

        var pfx = Math.exp(10);
        var fx = fnc(x);
        var pidx = 1;
        while (!convergence) {

            var indicies = optimjs.shuffleIndiciesOf(x);

            convergence = true;

            // Perform update over all of the variables in random order
            for (var i = 0; i < indicies.length; i++) {

                x[indicies[i]] += 1e-6;
                var fxi = fnc(x);
                x[indicies[i]] -= 1e-6;
                var dx = (fxi - fx) / 1e-6;

                if (Math.abs(dx) > eps) {
                    convergence = false;
                }

                x[indicies[i]] = x[indicies[i]] - alpha * dx;
                fx = fnc(x);

            }

            // a simple step size selection rule. Near x function acts linear
            // (this is assumed at least) and thus very small values of alpha
            // should lead to (small) improvement. Increasing alpha would
            // yield better improvement up to certain alpha size.

            alpha = pfx > fx ? alpha * 1.1 : alpha * 0.7;
            pfx = fx;

            pidx--;
            if (pidx === 0) {
                pidx = 1;
            }

        }

        var solution = {};
        solution.argument = x;
        solution.fncvalue = fx;

        return solution;

    };

    exports.minimize_GradientDescent = function (fnc, grd, x0) {
        // fnc: function which takes array of size N as an input
        // grd: gradient (array of size N) of function for some input
        // x0: array or real numbers of size N;
        // serves as initialization of algorithm.

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at found optimum
        var x = x0.slice();

        var convergence = false;
        var eps = 1e-3;
        var alpha = 0.01;

        var pfx = fnc(x);

        while (!convergence) {
            var g = grd(x);
            convergence = optimjs.vect_max_abs_x_less_eps(g, eps);

            if (convergence) {
                break;
            }

            var repeat = true;

            // a simple step size selection rule. Near x function acts linear
            // (this is assumed at least) and thus very small values of alpha
            // should lead to (small) improvement. Increasing alpha would
            // yield better improvement up to certain alpha size.

            while (repeat) {
                var xn = x.slice();
                optimjs.vect_x_pluseq_ag(xn, -alpha, g); // perform step
                var fx = fnc(xn);

                repeat = pfx < fx;
                // this automatically selects step size
                alpha = repeat ? alpha * 0.7 : alpha * 1.1;
            }

            x = xn;
            pfx = fx;

        }

        var solution = {};
        solution.argument = x;
        solution.fncvalue = fx;
        return solution;

    };

    exports.minimize_L_BFGS = function (fnc, grd, x0) {
        // fnc: function which takes array of size N as an input
        // grd: gradient (array of size N) of function for some input
        // x0: array or real numbers of size N;
        // serves as initialization of algorithm.

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at found optimum
        var x = x0.slice();

        var eps = 1e-3; // max abs value of gradient component for termination
        var alpha = 0.001; // initial step size
        var m = 5; // history size to keep for Hessian approximation

        var pfx = fnc(x);
        var s = []; // this is needed for lbfgs procedure
        var y = [];
        var ro = [];

        var g = grd(x);
        var direction = g.slice();
        var convergence = false;
        while (!convergence) {

            var xn = x.slice();
            optimjs.vect_x_pluseq_ag(xn, alpha, direction); // perform step
            var fx = fnc(xn);
            alpha = pfx < fx ? alpha * 0.5 : alpha * 1.2; // magic!

            //  < ================= apply limited memory BFGS procedure ================= >
            var gn = grd(xn);

            if (optimjs.vect_max_abs_x_less_eps(gn, eps)) {
                break;
            }

            var dx = optimjs.vect_a_minus_b(xn, x);
            var dg = optimjs.vect_a_minus_b(gn, g);

            s.unshift(dx);
            y.unshift(dg);
            var tmp = 1 / (optimjs.dot(dx, dg));
            ro.unshift(tmp);

            if (s.length > m) {
                s.pop();
                y.pop();
                ro.pop();
            }

            var r = g.slice();
            var a = new Array(s.length);

            for (var i = 0; i < s.length; i++) {
                var pi = 1 / (optimjs.dot(s[i], y[i]));
                a[i] = pi * optimjs.dot(s[i], r);
                optimjs.vect_x_pluseq_ag(r, -a[i], y[i]);
            }

            // perform Hessian scaling
            var scale = optimjs.dot(dx, dg) / optimjs.dot(dg, dg);
            for (var i = 0; i < r.length; i++) {
                r[i] = r[i] * scale;
            }

            for (var i = 0; i < s.length; i++) {
                var j = s.length - i - 1;
                var pj = 1 / (optimjs.dot(s[j], y[j]));
                var beta = pj * optimjs.dot(y[j], r);
                optimjs.vect_x_pluseq_ag(r, (a[j] - beta), s[j]);
            }
            direction = r.slice();

            for (var i = 0; i < direction.length; i++) {
                if (isNaN(direction[i])) {
                    convergence = true; // something went wrong. Stop now
                }
            }

            //  < ================= apply limited memory BFGS procedure ================= >

            for (var i = 0; i < direction.length; i++) {
                direction[i] = -direction[i];
            }

            pfx = fx;
            x = xn;
            g = gn;

        }

        var solution = {};
        solution.argument = x;
        solution.fncvalue = fx;
        return solution;

    };

    exports.minimize_SGD = function (I, fnc, grd, W0){
        // This routine assumes that objective can be written as
        //          f(W) = \sum_{i \in [n]} g(W)
        // and using this poperty gradient is updated for every separate i, which results in speedups.
        //
        // inputs:
        // I: array of indicies of all of points in the dataset (e.g. 1 ... 1000)
        // fnc: function which takes as input parameter W and index of point with index i
        // grd: gradient over parameter W of fnc for point with index i
        // W0: initial value of parameters of fnc

        // solution is a struct, with fields:
        // argument: solution argument
        // fncvalue: function value at optimum

        var splitp = Math.floor(I.length*0.7); // 70 % is used for training , rest for validation

        var Iv = I.slice(splitp+1);
        var I = I.slice(0, splitp);

        var W = W0.slice();

        var alpha = 0.00001; // initial alpha guess
        var checks = 5; // validation checks until stopping
        var fmin = Math.exp(30); // initial minimum value (something big)
        var maxiternum = 70; // maximum number of iterations

        var Wbest = W.slice();
        var idx = 0;

        while(checks > 0){
            idx ++;
            // perform gradient update
            for (var i = 0; i < I.length; i++) {
                var gr = grd(W,I[i]);
                optimjs.vect_x_pluseq_ag(W, -alpha, gr);
            }

            var floc = 0;

            // perform cross - validation
            for (var i = 0; i < Iv.length; i++) {
                var fv = fnc(W,Iv[i]);
                floc += fv;
            }

            if (floc < fmin) {
                fmin = floc;
                Wbest = W.slice();
                alpha *= 1.1;
                checks = 10;
            }
            else
            {
                checks -= 1;
                alpha *= 0.5;
            }

            // log the current state
            console.log("epoch with fv = " + fmin + " alpha= " + alpha);
            if (idx > maxiternum) {
                break;
            }
        }

        var solution = {};
        solution.argument = Wbest.slice();
        solution.fncvalue = fmin;
        return solution;

    };

    exports.numerical_gradient = function (fnc, x) {
        // can be used as for gradient check or its substitute. Gradient is approx. via forward difference
        var grad = x.slice();
        var fx = fnc(x);
        var h = 1e-6; // step size

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

            // approximation using simple forward difference
            x[i] += h;
            var fxi = fnc(x);
            x[i] -= h;

            grad[i] = (fxi - fx) / h;
        }
        return grad;
    };

    // some vector operations used in all of the optimization procedures above

    exports.shuffleIndiciesOf = function (array) {
        // returns shuffled indicies of arrray
        var idx = [];
        for (var i = 0; i < array.length; i++) {
            idx.push(i);
        }
        for (var i = 0; i < array.length; i++) {
            var j = Math.floor(Math.random() * i);
            var tmp = idx[i];
            idx[i] = idx[j];
            idx[j] = tmp;
        }
        return idx;
    };

    exports.dot = function (a, b) {
        // computes dot product
        var result = 0;
        for (var i = 0; i < a.length; i++) {
            result += a[i] * b[i];
        }
        return result;

    };

    exports.vect_x_times_c = function (x, c) {
        // vector times constant
        var r = x.slice();
        for (var i = 0; i < x.length; i++) {
            r[i]  = r[i] * c;
        }
        return r;

    };

    exports.vect_a_minus_b = function (a, b) {
        // vector difference
        var result = new Array(a.length);
        for (var i = 0; i < a.length; i++) {
            result[i] = a[i] - b[i];
        }
        return result;

    };

    exports.vect_x_pluseq_ag = function (x, a, g) {
        // used for fixed step size updating value of x
        for (var i = 0; i < x.length; i++) {
            x[i] = x[i] + a * g[i];
        }

        return x;

    };

    exports.vect_max_abs_x_less_eps = function (x, eps) {
        // this procedure is used for stopping criterion check
        for (var i = 0; i < x.length; i++) {
            if (Math.abs(x[i]) >= eps) {
                return false;
            }
        }
        return true;
    };

    return exports;

})(typeof module != 'undefined' && module.exports);  // add exports to module.exports if in node.js
<|endoftext|>


<commit_before>var RestClient = require('carbon-client')
var util = require('util')
var fibrous = require('fibrous');

/****************************************************************************************************
 * monkey patch the endpoint class to support sync get/post/put/delete/head/patch
 */

var Endpoint = RestClient.super_

/****************************************************************************************************
 * syncifyClassMethod
 *
 * @param clazz the class
 * @param methodName name of the method to syncify
 */

function syncifyClassMethod(clazz, methodName){

  var asyncMethod = clazz.prototype[methodName]

  clazz.prototype[methodName] = function() {
    // if last argument is a callback then run async
    if (arguments && (typeof(arguments[arguments.length-1]) === 'function') ) {
      asyncMethod.apply(this, arguments);
    } else { // sync call!
      return asyncMethod.sync.apply(this, arguments);
    }

  }

}

// syncify all Endpoint methods

var ENDPOINT_METHODS = [
  "get",
  "post",
  "head",
  "put",
  "delete",
  "patch"
]
ENDPOINT_METHODS.forEach(function(m) { syncifyClassMethod(Endpoint, m) })

// syncify Collection methods
var COLLECTION_METHODS = [
  "find",
  "insert",
  "update",
  "remove"
]

var Collection = Endpoint.collectionClass
COLLECTION_METHODS.forEach(function(m) { syncifyClassMethod(Collection, m) })

/****************************************************************************************************
 * exports
 */
module.exports = RestClient
<commit_msg>returning a cursor on collection.find()<commit_after>var RestClient = require('carbon-client')
var util = require('util')
var fibrous = require('fibrous');

/****************************************************************************************************
 * monkey patch the endpoint class to support sync get/post/put/delete/head/patch
 */

var Endpoint = RestClient.super_

/****************************************************************************************************
 * syncifyClassMethod
 *
 * @param clazz the class
 * @param methodName name of the method to syncify
 */

function syncifyClassMethod(clazz, methodName){

  var asyncMethod = clazz.prototype[methodName]

  clazz.prototype[methodName] = function() {
    // if last argument is a callback then run async
    if (arguments && (typeof(arguments[arguments.length-1]) === 'function') ) {
      asyncMethod.apply(this, arguments);
    } else { // sync call!
      return asyncMethod.sync.apply(this, arguments);
    }

  }

}

// syncify all Endpoint methods

var ENDPOINT_METHODS = [
  "get",
  "post",
  "head",
  "put",
  "delete",
  "patch"
]
ENDPOINT_METHODS.forEach(function(m) { syncifyClassMethod(Endpoint, m) })

// syncify Collection methods
var COLLECTION_METHODS = [
  "insert",
  "update",
  "remove"
]

var Collection = Endpoint.collectionClass
var CollectionCursor = Collection.collectionCursorClass
COLLECTION_METHODS.forEach(function(m) { syncifyClassMethod(Collection, m) })

syncifyClassMethod(CollectionCursor, "next")

/****************************************************************************************************
 * exports
 */
module.exports = RestClient
<|endoftext|>


<commit_before>//
// Please update "commments" to "submit point"
//

'use strict';

var assertionUrl = 'https://3pp.io:4343/persona';
var postPointUrl = 'https://3pp.io:4343/post/point';
var serviceListUrl = 'https://tosdr.org/index/services.json';

$(document).ready(function(){
  var loggedInUser = null;
  var loadTimer = setTimeout(function() {
    showPersona();
  }, 3000);
  function logout(){
    loggedInUser = null;
    var response = $.post(assertionUrl + '/logout');
    response.always(showPersona);
  }

  function verify(assertion){
    if (loadTimer) {
      clearTimeout(loadTimer);
      loadTimer = null;
    }
    showSigningIn();
    var response = $.ajax(assertionUrl + '/verify', {data: {assertion: assertion}, type: 'POST', xhrFields: {withCredentials: true}});
    response.done(login);
    response.fail(function(){
      navigator.id.logout();
    });
  }

  function login(data){
    showSubmitForm(data.email);
  }

  navigator.id.watch({
    loggedInUser: loggedInUser,
    onlogin: verify,
    onlogout: logout
  });

  var numServices = 1;
  function showServiceFields() {
    var str = '          <input type="text" placeholder="service">';
    $('.serviceFields').html(str);
  }

  function showPersona(){
    $('#form').html('

To submit a point, please sign in — you can use an existing email address.

');
    $('.signinButton').click(function(){ navigator.id.request(); }).css('cursor', 'pointer');
  }

  function showSigningIn(){
    $('#form').html('

(signing you in...)

');
  }

  function showLoading(){
    $('#form').html('
(loading...)
');
  }

  // This is ugly, too much html here
  //
  //

  function showSubmitForm(email){
    $('#form').html('<form class="submitForm">' +
        '' +
        '<fieldset>' +
        '<legend>Submit a point about a clause</legend>' +

        'Title<input type="text" id="titleField" name="title" required />' +

        'Service(s)<input id="servicesField" name="services" required class="input-large" />' +

'          Service(s) to which the point applies' +
'                                       ' +
'          <button class="btn btn-inline" type="button" onclick="addServiceField();">Add one</button>' +
'' +

        '<input type="radio" name="point" id="pointGood" value="good" />Good<input type="radio" name="point" id="pointNeutral" value="neutral" />Neutral<input type="radio" required name="point" id="pointBad" value="bad" />Bad<input type="radio" name="point" id="pointBlocker" value="blocker" />Blocker' +

        'Topic<input id="topicsField" name="topics" required class="input-large" />' +

'          <select id="topics">' +
'            <option>Select a topic</option>' +
'            <option>Scope of the copyright license</option>' +
'            <option>Changes</option>' +
'          </select>' +

        'Can you summarise this clause? Parsed as Markdown<textarea name="summary" id="summaryField" required class="input-xxlarge" placeholder="This is the summary, the recap of what the clause in the terms do. It is usually a very difficult exercise. Try not to make more than 5 sentences, but sometimes more is needed. Be creative. Feel free to include some quotes from the terms directly. It is very important that it is easy to understand and written in plain English."></textarea>' +

        '</fieldset>' +
        '
' +
        '<button class="btn btn-primary" id="postPoint">Send</button>' +
        'Submitting as ' + email + ' (Not your email address? Log out!)' +

        '
' +
        '</form>');
    showServiceFields();
    var response = $.get(serviceListUrl, 'json');
    response.done(function(services){
      services = $.map(services, function(value, key){
        return key;
      });
      // Note: the width should be 'element', but for some reason it receives a different width than other input elements
      $('#servicesField').select2({tags: services, width: '220px', containerCss: {'border-radius': '4px'}});
    });

    $('.submitForm').submit(function(e){
      var response = $.ajax(postPointUrl, {data: {title: $('#titleField').val(), point: $('input[name=point]:checked', '.submitForm').val(), services: $('#servicesField').val(), summary: $('#summaryField').val()}, type: 'POST', xhrFields: {withCredentials: true}});
      response.done(showConfirmation);
      response.fail(showError);
      e.preventDefault();
    });
    $('.signoutButton').click(function(){ navigator.id.logout(); });
  }

  function showConfirmation(){
    $('#form').html('Your point has been submitted. Thank you! '
        +'submit another one or '
        +'see the list.');
  }

  function showError(){
    $('#form').prepend('
<button type="button" class="close" data-dismiss="alert">×</button>Something went wrong saving your point, did you fill in all the fields?
');
  }
  showLoading();
});
<commit_msg>rename form<commit_after>//
// Please update "commments" to "submit point"
//

'use strict';

var assertionUrl = 'https://3pp.io:4343/persona';
var postPointUrl = 'https://3pp.io:4343/post/point';
var serviceListUrl = 'https://tosdr.org/index/services.json';

$(document).ready(function(){
  var loggedInUser = null;
  var loadTimer = setTimeout(function() {
    showPersona();
  }, 3000);
  function logout(){
    loggedInUser = null;
    var response = $.post(assertionUrl + '/logout');
    response.always(showPersona);
  }

  function verify(assertion){
    if (loadTimer) {
      clearTimeout(loadTimer);
      loadTimer = null;
    }
    showSigningIn();
    var response = $.ajax(assertionUrl + '/verify', {data: {assertion: assertion}, type: 'POST', xhrFields: {withCredentials: true}});
    response.done(login);
    response.fail(function(){
      navigator.id.logout();
    });
  }

  function login(data){
    showSubmitForm(data.email);
  }

  navigator.id.watch({
    loggedInUser: loggedInUser,
    onlogin: verify,
    onlogout: logout
  });

  var numServices = 1;
  function showServiceFields() {
    var str = '          <input type="text" placeholder="service">';
    $('.serviceFields').html(str);
  }

  function showPersona(){
    $('#submit-point-form').html('

To submit a point, please sign in — you can use an existing email address.

');
    $('.signinButton').click(function(){ navigator.id.request(); }).css('cursor', 'pointer');
  }

  function showSigningIn(){
    $('#submit-point-form').html('

(signing you in...)

');
  }

  function showLoading(){
    $('#submit-point-form').html('
(loading...)
');
  }

  // This is ugly, too much html here
  //
  //

  function showSubmitForm(email){
    $('#submit-point-form').html('<form class="submitForm">' +
        '' +
        '<fieldset>' +
        '<legend>Submit a point about a clause</legend>' +

        'Title<input type="text" id="titleField" name="title" required />' +

        'Service(s)<input id="servicesField" name="services" required class="input-large" />' +

'          Service(s) to which the point applies' +
'                                       ' +
'          <button class="btn btn-inline" type="button" onclick="addServiceField();">Add one</button>' +
'' +

        '<input type="radio" name="point" id="pointGood" value="good" />Good<input type="radio" name="point" id="pointNeutral" value="neutral" />Neutral<input type="radio" required name="point" id="pointBad" value="bad" />Bad<input type="radio" name="point" id="pointBlocker" value="blocker" />Blocker' +

        'Topic<input id="topicsField" name="topics" required class="input-large" />' +

'          <select id="topics">' +
'            <option>Select a topic</option>' +
'            <option>Scope of the copyright license</option>' +
'            <option>Changes</option>' +
'          </select>' +

        'Can you summarise this clause? Parsed as Markdown<textarea name="summary" id="summaryField" required class="input-xxlarge" placeholder="This is the summary, the recap of what the clause in the terms do. It is usually a very difficult exercise. Try not to make more than 5 sentences, but sometimes more is needed. Be creative. Feel free to include some quotes from the terms directly. It is very important that it is easy to understand and written in plain English."></textarea>' +

        '</fieldset>' +
        '
' +
        '<button class="btn btn-primary" id="postPoint">Send</button>' +
        'Submitting as ' + email + ' (Not your email address? Log out!)' +

        '
' +
        '</form>');
    showServiceFields();
    var response = $.get(serviceListUrl, 'json');
    response.done(function(services){
      services = $.map(services, function(value, key){
        return key;
      });
      // Note: the width should be 'element', but for some reason it receives a different width than other input elements
      $('#servicesField').select2({tags: services, width: '220px', containerCss: {'border-radius': '4px'}});
    });

    $('.submitForm').submit(function(e){
      var response = $.ajax(postPointUrl, {data: {title: $('#titleField').val(), point: $('input[name=point]:checked', '.submitForm').val(), services: $('#servicesField').val(), summary: $('#summaryField').val()}, type: 'POST', xhrFields: {withCredentials: true}});
      response.done(showConfirmation);
      response.fail(showError);
      e.preventDefault();
    });
    $('.signoutButton').click(function(){ navigator.id.logout(); });
  }

  function showConfirmation(){
    $('#submit-point-form').html('Your point has been submitted. Thank you! '
        +'submit another one or '
        +'see the list.');
  }

  function showError(){
    $('#submit-point-form').prepend('
<button type="button" class="close" data-dismiss="alert">×</button>Something went wrong saving your point, did you fill in all the fields?
');
  }
  showLoading();
});
<|endoftext|>


<commit_before>/*
 Trivial Components (https://github.com/trivial-components/trivial-components)

 Copyright 2015 Yann Massard (https://github.com/yamass) and other contributors

 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.
 */
(function (factory) {
        "use strict";

        if (typeof define === 'function' && define.amd) {
            // Define as an AMD module if possible
            define('trivial-tree', ['trivial-core', 'jquery', 'mustache'], factory);
        } else if (typeof exports === 'object') {
            // Node/CommonJS
            module.exports = factory(require('trivial-core'), require('jquery'), require('mustache'));
        } else if (jQuery && !jQuery.fn.Trivialtree) {
            // Define using browser globals otherwise
            // Prevent multiple instantiations if the script is loaded twice
            window.TrivialTree = factory(TrivialComponents, jQuery, Mustache);
        }
    }(function (TrivialComponents, $, Mustache) {

        var keyCodes = TrivialComponents.keyCodes;

        function TrivialTree(originalInput, options) {
            var me = this;

            options = options || {};
            var defaultOptions = {
                valueProperty: 'id',
                childrenProperty: "children",
                lazyChildrenFlagProperty: "hasLazyChildren",
                searchBarMode: 'show-if-filled', // none, show-if-filled, always-visible
                lazyChildrenQueryFunction: function (node, resultCallback) {
                    resultCallback([])
                },
                expandedProperty: 'expanded',
                templates: [TrivialComponents.iconSingleLineTemplate],
                spinnerTemplate: TrivialComponents.defaultSpinnerTemplate,
                noEntriesTemplate: TrivialComponents.defaultNoEntriesTemplate,
                entries: null,
                selectedEntryId: null,
                matchingOptions: {
                    matchingMode: 'contains',
                    ignoreCase: true,
                    maxLevenshteinDistance: 2
                },
                directSelectionViaArrowKeys: false,
                performanceOptimizationSettings: {
                    toManyVisibleItemsRenderDelay: 750,
                    toManyVisibleItemsThreshold: 75
                }
            };
            var config = $.extend(defaultOptions, options);
            config.queryFunction = config.queryFunction || TrivialComponents.defaultTreeQueryFunctionFactory(config.entries || [], config.templates, config.matchingOptions, config.childrenProperty, config.expandedProperty);

            this.onSelectedEntryChanged = new TrivialComponents.Event();
            this.onNodeExpansionStateChanged = new TrivialComponents.Event();

            var treeBox;
            var entries = config.entries;
            var selectedEntryId;

            var $spinners = $();
            var $originalInput = $(originalInput).addClass("tr-original-input");
            var $componentWrapper = $('
').insertAfter($originalInput);
            if (config.searchBarMode !== 'always-visible') {
                $componentWrapper.addClass(config.showSearchField ? "" : "hide-searchfield");
            }
            $componentWrapper.keydown(function (e) {
                if (e.which == keyCodes.tab || TrivialComponents.isModifierKey(e)) {
                    return; // tab or modifier key was pressed...
                }
                if ($editor.is(':visible') && keyCodes.specialKeys.indexOf(e.which) === -1) {
                    $editor.focus();
                }
                if (e.which == keyCodes.up_arrow || e.which == keyCodes.down_arrow) {
                    var direction = e.which == keyCodes.up_arrow ? -1 : 1;
                    if (entries != null) {
                        if (config.directSelectionViaArrowKeys) {
                            treeBox.selectNextEntry(direction)
                        } else {
                            treeBox.highlightNextEntry(direction);
                        }
                        return false; // some browsers move the caret to the beginning on up key
                    }
                } else if (e.which == keyCodes.left_arrow || e.which == keyCodes.right_arrow) {
                    treeBox.setHighlightedNodeExpanded(e.which == keyCodes.right_arrow);
                } else if (e.which == keyCodes.enter) {
                    treeBox.setSelectedEntry(treeBox.getHighlightedEntry()[config.valueProperty]);
                } else if (e.which == keyCodes.escape) {
                    $editor.val("");
                    query();
                    $componentWrapper.focus();
                } else {
                    query(1);
                }
            });
            var $tree = $('

').appendTo($componentWrapper);
            var $editor = $('<input type="text" class="tr-tree-editor tr-editor"/>')
                .prependTo($componentWrapper)
                .attr("tabindex", $originalInput.attr("-1"))
                .focus(function () {
                    $componentWrapper.addClass('focus');
                })
                .blur(function () {
                    $componentWrapper.removeClass('focus');
                })
                .keydown(function (e) {
                    if (e.which == keyCodes.left_arrow || e.which == keyCodes.right_arrow) {
                        // expand the currently highlighted node.
                        var changedExpandedState = treeBox.setHighlightedNodeExpanded(e.which == keyCodes.right_arrow);
                        if (changedExpandedState) {
                            return false;
                        } else {
                            return; // let the user navigate freely left and right...
                        }
                    }
                })
                .on('keyup change', function () {
                    if (config.searchBarMode === 'show-if-filled') {
                        if ($editor.val()) {
                            $componentWrapper.removeClass('hide-searchfield');
                        } else {
                            $componentWrapper.addClass('hide-searchfield');
                        }
                    }
                });
            if (config.searchBarMode === 'none') {
                $editor.css("display", "none");
            }

            if ($originalInput.attr("placeholder")) {
                $editor.attr("placeholder", $originalInput.attr("placeholder"));
            }
            if ($originalInput.attr("tabindex")) {
                $componentWrapper.attr("tabindex", $originalInput.attr("tabindex"));
            }
            if ($originalInput.attr("autofocus")) {
                $componentWrapper.focus();
            }

            treeBox = $tree.TrivialTreeBox(config);
            treeBox.onNodeExpansionStateChanged.addListener(function (node) {
                me.onNodeExpansionStateChanged.fire(node);
            });
            treeBox.$.change(function () {
                var selectedTreeBoxEntry = treeBox.getSelectedEntry();
                if (selectedTreeBoxEntry) {
                    selectEntry(selectedTreeBoxEntry);
                }
            });

            selectEntry((config.selectedEntryId !== undefined && config.selectedEntryId !== null) ? findEntryById(config.selectedEntryId) : null);

            function updateEntries(newEntries) {
                entries = newEntries;
                $spinners.remove();
                $spinners = $();
                treeBox.updateEntries(newEntries);
            }

            var processUpdateTimer;

            function query(highlightDirection) {
                if (config.searchBarMode === 'always-visible' || config.searchBarMode === 'show-if-filled') {
                    var $spinner = $(config.spinnerTemplate).appendTo($tree);
                    $spinners = $spinners.add($spinner);

                    // call queryFunction asynchronously to be sure the input field has been updated before the result callback is called. Note: the query() method is called on keydown...
                    setTimeout(function () {
                        config.queryFunction($editor.val(), {
                            completeInputString: $editor.val(),
                            currentlySelectedEntry: findEntryById(selectedEntryId)
                        }, function (newEntries) {
                            function processUpdate() {
                                updateEntries(newEntries);
                                if ($editor.val().length > 0) {
                                    treeBox.highlightTextMatches($editor.val());
                                    if (!config.directSelectionViaArrowKeys) {
                                        treeBox.highlightNextMatchingEntry(highlightDirection);
                                    }
                                }
                                treeBox.revealSelectedEntry();
                            }

                            clearTimeout(processUpdateTimer);
                            if (countVisibleEntries(newEntries) < config.performanceOptimizationSettings.toManyVisibleItemsThreshold) {
                                processUpdate();
                            } else {
                                processUpdateTimer = setTimeout(processUpdate, config.performanceOptimizationSettings.toManyVisibleItemsRenderDelay);
                            }
                        });
                    }, 0);
                }
            }

            function countVisibleEntries(entries) {
                function countVisibleChildrenAndSelf(node) {
                    if (node[config.expandedProperty] && node[config.childrenProperty]) {
                        return node[config.childrenProperty].map(function (entry) {
                            return countVisibleChildrenAndSelf(entry);
                        }).reduce(function (a, b) {
                            return a + b;
                        }, 0) + 1;
                    } else {
                        return 1;
                    }
                }

                return entries.map(function (entry) {
                    return countVisibleChildrenAndSelf(entry);
                }).reduce(function (a, b) {
                    return a + b;
                }, 0);
            }

            function findEntries(filterFunction) {
                function findEntriesInSubTree(node, listOfFoundEntries) {
                    if (filterFunction.call(this, node)) {
                        listOfFoundEntries.push(node);
                    }
                    if (node[config.childrenProperty]) {
                        for (var i = 0; i < node[config.childrenProperty].length; i++) {
                            var child = node[config.childrenProperty][i];
                            findEntriesInSubTree(child, listOfFoundEntries);
                        }
                    }
                }

                var matchingEntries = [];
                for (var i = 0; i < entries.length; i++) {
                    var rootEntry = entries[i];
                    findEntriesInSubTree(rootEntry, matchingEntries);
                }
                return matchingEntries;
            }

            function findEntryById(id) {
                return findEntries(function (entry) {
                    return entry[config.valueProperty] == id
                })[0];
            }

            function selectEntry(entry) {
                selectedEntryId = entry ? entry[config.valueProperty] : null;
                $originalInput.val(entry ? entry[config.valueProperty] : null);
                fireChangeEvents(entry);
            }

            function fireChangeEvents(entry) {
                $originalInput.trigger("change");
                $componentWrapper.trigger("change");
                me.onSelectedEntryChanged.fire(entry);
            }

            this.$ = $componentWrapper;
            $componentWrapper[0].trivialTree = this;

            this.updateEntries = updateEntries;
            this.getSelectedEntry = treeBox.getSelectedEntry;
            this.updateChildren = treeBox.updateChildren;
            this.updateNode = treeBox.updateNode;
            this.removeNode = treeBox.removeNode;
            this.addNode = treeBox.addNode;
            this.destroy = function() {
                $originalInput.removeClass('tr-original-input').insertBefore($componentWrapper);
                $componentWrapper.remove();
            };
        }

        TrivialComponents.registerJqueryPlugin(TrivialTree, "TrivialTree", "tr-tree");

        return $.fn.TrivialTree;
    })
);
<commit_msg>tree: selectNode by id<commit_after>/*
 Trivial Components (https://github.com/trivial-components/trivial-components)

 Copyright 2015 Yann Massard (https://github.com/yamass) and other contributors

 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.
 */
(function (factory) {
        "use strict";

        if (typeof define === 'function' && define.amd) {
            // Define as an AMD module if possible
            define('trivial-tree', ['trivial-core', 'jquery', 'mustache'], factory);
        } else if (typeof exports === 'object') {
            // Node/CommonJS
            module.exports = factory(require('trivial-core'), require('jquery'), require('mustache'));
        } else if (jQuery && !jQuery.fn.Trivialtree) {
            // Define using browser globals otherwise
            // Prevent multiple instantiations if the script is loaded twice
            window.TrivialTree = factory(TrivialComponents, jQuery, Mustache);
        }
    }(function (TrivialComponents, $, Mustache) {

        var keyCodes = TrivialComponents.keyCodes;

        function TrivialTree(originalInput, options) {
            var me = this;

            options = options || {};
            var defaultOptions = {
                valueProperty: 'id',
                childrenProperty: "children",
                lazyChildrenFlagProperty: "hasLazyChildren",
                searchBarMode: 'show-if-filled', // none, show-if-filled, always-visible
                lazyChildrenQueryFunction: function (node, resultCallback) {
                    resultCallback([])
                },
                expandedProperty: 'expanded',
                templates: [TrivialComponents.iconSingleLineTemplate],
                spinnerTemplate: TrivialComponents.defaultSpinnerTemplate,
                noEntriesTemplate: TrivialComponents.defaultNoEntriesTemplate,
                entries: null,
                selectedEntryId: null,
                matchingOptions: {
                    matchingMode: 'contains',
                    ignoreCase: true,
                    maxLevenshteinDistance: 2
                },
                directSelectionViaArrowKeys: false,
                performanceOptimizationSettings: {
                    toManyVisibleItemsRenderDelay: 750,
                    toManyVisibleItemsThreshold: 75
                }
            };
            var config = $.extend(defaultOptions, options);
            config.queryFunction = config.queryFunction || TrivialComponents.defaultTreeQueryFunctionFactory(config.entries || [], config.templates, config.matchingOptions, config.childrenProperty, config.expandedProperty);

            this.onSelectedEntryChanged = new TrivialComponents.Event();
            this.onNodeExpansionStateChanged = new TrivialComponents.Event();

            var treeBox;
            var entries = config.entries;
            var selectedEntryId;

            var $spinners = $();
            var $originalInput = $(originalInput).addClass("tr-original-input");
            var $componentWrapper = $('
').insertAfter($originalInput);
            if (config.searchBarMode !== 'always-visible') {
                $componentWrapper.addClass(config.showSearchField ? "" : "hide-searchfield");
            }
            $componentWrapper.keydown(function (e) {
                if (e.which == keyCodes.tab || TrivialComponents.isModifierKey(e)) {
                    return; // tab or modifier key was pressed...
                }
                if ($editor.is(':visible') && keyCodes.specialKeys.indexOf(e.which) === -1) {
                    $editor.focus();
                }
                if (e.which == keyCodes.up_arrow || e.which == keyCodes.down_arrow) {
                    var direction = e.which == keyCodes.up_arrow ? -1 : 1;
                    if (entries != null) {
                        if (config.directSelectionViaArrowKeys) {
                            treeBox.selectNextEntry(direction)
                        } else {
                            treeBox.highlightNextEntry(direction);
                        }
                        return false; // some browsers move the caret to the beginning on up key
                    }
                } else if (e.which == keyCodes.left_arrow || e.which == keyCodes.right_arrow) {
                    treeBox.setHighlightedNodeExpanded(e.which == keyCodes.right_arrow);
                } else if (e.which == keyCodes.enter) {
                    treeBox.setSelectedEntry(treeBox.getHighlightedEntry()[config.valueProperty]);
                } else if (e.which == keyCodes.escape) {
                    $editor.val("");
                    query();
                    $componentWrapper.focus();
                } else {
                    query(1);
                }
            });
            var $tree = $('

').appendTo($componentWrapper);
            var $editor = $('<input type="text" class="tr-tree-editor tr-editor"/>')
                .prependTo($componentWrapper)
                .attr("tabindex", $originalInput.attr("-1"))
                .focus(function () {
                    $componentWrapper.addClass('focus');
                })
                .blur(function () {
                    $componentWrapper.removeClass('focus');
                })
                .keydown(function (e) {
                    if (e.which == keyCodes.left_arrow || e.which == keyCodes.right_arrow) {
                        // expand the currently highlighted node.
                        var changedExpandedState = treeBox.setHighlightedNodeExpanded(e.which == keyCodes.right_arrow);
                        if (changedExpandedState) {
                            return false;
                        } else {
                            return; // let the user navigate freely left and right...
                        }
                    }
                })
                .on('keyup change', function () {
                    if (config.searchBarMode === 'show-if-filled') {
                        if ($editor.val()) {
                            $componentWrapper.removeClass('hide-searchfield');
                        } else {
                            $componentWrapper.addClass('hide-searchfield');
                        }
                    }
                });
            if (config.searchBarMode === 'none') {
                $editor.css("display", "none");
            }

            if ($originalInput.attr("placeholder")) {
                $editor.attr("placeholder", $originalInput.attr("placeholder"));
            }
            if ($originalInput.attr("tabindex")) {
                $componentWrapper.attr("tabindex", $originalInput.attr("tabindex"));
            }
            if ($originalInput.attr("autofocus")) {
                $componentWrapper.focus();
            }

            treeBox = $tree.TrivialTreeBox(config);
            treeBox.onNodeExpansionStateChanged.addListener(function (node) {
                me.onNodeExpansionStateChanged.fire(node);
            });
            treeBox.$.change(function () {
                var selectedTreeBoxEntry = treeBox.getSelectedEntry();
                if (selectedTreeBoxEntry) {
                    selectEntry(selectedTreeBoxEntry);
                }
            });

            selectEntry((config.selectedEntryId !== undefined && config.selectedEntryId !== null) ? findEntryById(config.selectedEntryId) : null);

            function updateEntries(newEntries) {
                entries = newEntries;
                $spinners.remove();
                $spinners = $();
                treeBox.updateEntries(newEntries);
            }

            var processUpdateTimer;

            function query(highlightDirection) {
                if (config.searchBarMode === 'always-visible' || config.searchBarMode === 'show-if-filled') {
                    var $spinner = $(config.spinnerTemplate).appendTo($tree);
                    $spinners = $spinners.add($spinner);

                    // call queryFunction asynchronously to be sure the input field has been updated before the result callback is called. Note: the query() method is called on keydown...
                    setTimeout(function () {
                        config.queryFunction($editor.val(), {
                            completeInputString: $editor.val(),
                            currentlySelectedEntry: findEntryById(selectedEntryId)
                        }, function (newEntries) {
                            function processUpdate() {
                                updateEntries(newEntries);
                                if ($editor.val().length > 0) {
                                    treeBox.highlightTextMatches($editor.val());
                                    if (!config.directSelectionViaArrowKeys) {
                                        treeBox.highlightNextMatchingEntry(highlightDirection);
                                    }
                                }
                                treeBox.revealSelectedEntry();
                            }

                            clearTimeout(processUpdateTimer);
                            if (countVisibleEntries(newEntries) < config.performanceOptimizationSettings.toManyVisibleItemsThreshold) {
                                processUpdate();
                            } else {
                                processUpdateTimer = setTimeout(processUpdate, config.performanceOptimizationSettings.toManyVisibleItemsRenderDelay);
                            }
                        });
                    }, 0);
                }
            }

            function countVisibleEntries(entries) {
                function countVisibleChildrenAndSelf(node) {
                    if (node[config.expandedProperty] && node[config.childrenProperty]) {
                        return node[config.childrenProperty].map(function (entry) {
                            return countVisibleChildrenAndSelf(entry);
                        }).reduce(function (a, b) {
                            return a + b;
                        }, 0) + 1;
                    } else {
                        return 1;
                    }
                }

                return entries.map(function (entry) {
                    return countVisibleChildrenAndSelf(entry);
                }).reduce(function (a, b) {
                    return a + b;
                }, 0);
            }

            function findEntries(filterFunction) {
                function findEntriesInSubTree(node, listOfFoundEntries) {
                    if (filterFunction.call(this, node)) {
                        listOfFoundEntries.push(node);
                    }
                    if (node[config.childrenProperty]) {
                        for (var i = 0; i < node[config.childrenProperty].length; i++) {
                            var child = node[config.childrenProperty][i];
                            findEntriesInSubTree(child, listOfFoundEntries);
                        }
                    }
                }

                var matchingEntries = [];
                for (var i = 0; i < entries.length; i++) {
                    var rootEntry = entries[i];
                    findEntriesInSubTree(rootEntry, matchingEntries);
                }
                return matchingEntries;
            }

            function findEntryById(id) {
                return findEntries(function (entry) {
                    return entry[config.valueProperty] == id
                })[0];
            }

            function selectEntry(entry) {
                selectedEntryId = entry ? entry[config.valueProperty] : null;
                $originalInput.val(entry ? entry[config.valueProperty] : null);
                fireChangeEvents(entry);
            }

            function fireChangeEvents(entry) {
                $originalInput.trigger("change");
                $componentWrapper.trigger("change");
                me.onSelectedEntryChanged.fire(entry);
            }

            this.$ = $componentWrapper;
            $componentWrapper[0].trivialTree = this;

            this.updateEntries = updateEntries;
            this.getSelectedEntry = treeBox.getSelectedEntry;
            this.updateChildren = treeBox.updateChildren;
            this.updateNode = treeBox.updateNode;
            this.removeNode = treeBox.removeNode;
            this.addNode = treeBox.addNode;
            this.selectNode = function(nodeId) {
                treeBox.setSelectedEntry(nodeId);
            };
            this.destroy = function() {
                $originalInput.removeClass('tr-original-input').insertBefore($componentWrapper);
                $componentWrapper.remove();
            };
        }

        TrivialComponents.registerJqueryPlugin(TrivialTree, "TrivialTree", "tr-tree");

        return $.fn.TrivialTree;
    })
);
<|endoftext|>


<commit_before>var Manual =  Backbone.View.extend({
	el: '#load',

	events:{
		'click #manualBac': 'addBACLevel'
	},

	initialize: function(){
		this.user = new You();
		this.you = this.user.retrieveProfile();
		this.form = '<form role="form">
Alcohol Content Volume<input id="acv" name="acv" class="form-control" placeholder="0" required>

Ounzes<input id="oz" name="oz" class="form-control" placeholder="0" required>

Time<input id="time" type="number" min="0" max="24" class="form-control" required>

<button id="manualBac" class="btn btn-success">Calculate</button></form>
 Your BAC Level is
';

		this.manualBac = new BAC();
		//this.render();
	},

	render: function(){
		this.$el.html(this.form);
	},

	calcBac: function(){
		var acv = $('#acv').val();
		var oz = $('#oz').val();
		var time = $('#time').val();
		var bac = this.manualBac.calcBac(acv, oz, this.you.weight, this.you.genderBac, time);
		$('#BACLevel').empty();
		$('#BACLevel').append('
'+bac+'
');
		return bac;
	},

	getTime: function(){
		var date = new Date();
		var time = {
			hour: 0,
			min: 0
		};
		time.hour = date.getHours();
		time.min = date.getMinutes();

		var curTime = time.hour+':'+time.min;
		return curTime;
	},

	getDate: function(){
		var date2 = new Date();
		var date = {
			year: 0,
			month: 0,
			day:0
		};

		date.year = date2.getFullYear();
		date.month = date2.getMonth()+1;
		date.day = date2.getDate();

		var today = date.year+'-'+date.month+'-'+date.day;

		return today;
	},

	addBACLevel: function(){
		console.log(this.you);
		var bac = this.calcBac();
		var time = this.getTime();
		var date = this.getDate();

		var newBac = new PersonBAC({
			"date": date,
			"bacLevelHigh": bac,
			"bacLevelCur": 0,
			"lat": 0,
			"lng": 0,
			"timeStart": time,
			"timeFinish": time,
			"manualEnter": true
		});

		console.log(newBac);
	}
})<commit_msg>Now stores BAC Level in user object to be save in local storage<commit_after>var Manual =  Backbone.View.extend({
	el: '#load',

	events:{
		'click #manualBac': 'addBACLevel'
	},

	initialize: function(){
		this.user = new You();
		this.you = this.user.retrieveProfile();
		this.form = '<form role="form">
Alcohol Content Volume<input id="acv" name="acv" class="form-control" placeholder="0" required>

Ounzes<input id="oz" name="oz" class="form-control" placeholder="0" required>

Time<input id="time" type="number" min="0" max="24" class="form-control" required>

<button id="manualBac" class="btn btn-success">Calculate</button></form>
 Your BAC Level is
';

		this.manualBac = new BAC();
		//this.render();
	},

	render: function(){
		this.$el.html(this.form);
	},

	calcBac: function(){
		var acv = $('#acv').val();
		var oz = $('#oz').val();
		var time = $('#time').val();
		var bac = this.manualBac.calcBac(acv, oz, this.you.weight, this.you.genderBac, time);
		$('#BACLevel').empty();
		$('#BACLevel').append('
'+bac+'
');
		return bac;
	},

	getTime: function(){
		var date = new Date();
		var time = {
			hour: 0,
			min: 0
		};
		time.hour = date.getHours();
		time.min = date.getMinutes();

		var curTime = time.hour+':'+time.min;
		return curTime;
	},

	getDate: function(){
		var date2 = new Date();
		var date = {
			year: 0,
			month: 0,
			day:0
		};

		date.year = date2.getFullYear();
		date.month = date2.getMonth()+1;
		date.day = date2.getDate();

		var today = date.year+'-'+date.month+'-'+date.day;

		return today;
	},

	addBACLevel: function(){
		console.log(this.you);
		var bac = this.calcBac();
		var time = this.getTime();
		var date = this.getDate();

		var newBac = new PersonBAC({
			"date": date,
			"bacLevelHigh": bac,
			"bacLevelCur": 0,
			"lat": 0,
			"lng": 0,
			"timeStart": time,
			"timeFinish": time,
			"manualEnter": true
		});

		this.you.BACLevels.push(newBac);
		people.localStorage.update(this.you);

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


<commit_before>var request = require('request');
var AWS = require('aws-sdk');
var ec2 = new AWS.EC2({
  apiVersion: '2014-10-01'
, region: 'us-west-2'
});

var Instances = require('./Instances');
var Tags = require('./Tags');

var poolingStrategy;
module.exports.poolingStrategy = poolingStrategy;

const POOL_TAGS = [
  {
    Key: 'smake'
  , Value: 'pool'
  }
];
const SSHABLE_TAG = [
  {
    Key: 'sshable'
  , Value: 'false'
  }
];
const SSH_POLLING_INTERVAL = 30; // seconds

function InstancePool(strategy) {

  // Default to naive strategy.
  if (typeof(strategy) === 'undefined') {
    strategy = 'NaiveStrategy';
  }
  var _strategy = require('./poolingStrategies/' + strategy);
  this.poolingStrategy = new _strategy();
  poolingStrategy = new _strategy();
  this.ec2 = ec2;
}

// Returns the name of the pooling strategy currently in use
InstancePool.prototype.strategy = function(callback) {
  return this.poolingStrategy.constructor.name;
};

// Terminates the specified instance, interrupting any running
// jobs and destroying the attached EBS volume. Completion of
// this non-reversible process is signaled via the callback.
InstancePool.terminate = function(instanceId, callback) {
  var params = {
    InstanceIds: [instanceId]
  };
  ec2.terminateInstances(params, function(err, data) {
    if (err) {
      console.log(err, data);
    } else {
      callback(instanceId);
    }
  });
};

// Adds an instance of the specified type to the instance pool.
// Uses the default keypair. Completion signaled via callback.
InstancePool.create = function(type, _callback) {
  function callback(instanceId) {
    if (_callback) {
      _callback(instanceId);
    }
  }

  // Get ami list
  var amis = require('./defaultAmis.json');

  // Define instance paramaters
  var params = {
    ImageId: amis[type]
  , InstanceType: type
  , KeyName: 'smake'
  , MinCount: 1
  , MaxCount: 1
  };

  // Create the instance.
  ec2.runInstances(params, function(err, data) {
    if (err) {
      console.log('There was a problem creating the instance.', err);
      return;
    }

    // Apply pool tags to the new instance.
    var instanceId = data.Instances[0].InstanceId;
    var tags = POOL_TAGS;
    // tags.push(SSHABLE_TAG);
    Tags.applyTags(tags, instanceId, callback(instanceId));

    // tags = [];
    // tags.push(SSHABLE_TAG);
    console.log('sshable tag:', SSHABLE_TAG);
    Tags.applyTags(SSHABLE_TAG, instanceId, callback(instanceId));

    // We just notifed the caller that we were done, but there's a
    // bit more we should do. Doing it like this allows the caller
    // to continue chugging along while we wrap things up. This
    // enables the system to respond more quickly than it would otherwise.

    // Periodically check if the instance is accepting ssh connections.
    InstancePool.startSshPolling(instanceId);
  });
};

// Periodically check if it's possible to ssh into the instance.
// SSH_POLLING_INTERVAL determines the interval for polling.
// Checks immediately upon calling, and then at the specified interval.
InstancePool.startSshPolling = function(instanceId) {
  var pollingInterval = SSH_POLLING_INTERVAL * 1000; // Convert to ms
  var interval = setInterval(function() {
    Instances.canSsh(instanceId, function(sshable) {
      if (sshable) {
        InstancePool.onSshable(instanceId, interval);
      } else {
      }
    });
  }, pollingInterval);
};

// Called when it becomes possible to ssh into the instance.
// Sets the 'sshable' tag to 'true'
// Also cancels the setInterval function that was polling
// AWS to determine this.
InstancePool.onSshable = function(instanceId, interval) {
  if (interval) { clearInterval(interval); } // Stop polling.
  Tags.removeTags(SSHABLE_TAG, instanceId, function() {
    // var tags = SSHABLE_TAG.slice(0);
    // var tags = SSHABLE_TAG;
    var tags = [
      {
        Key: 'sshable'
      , Value: 'true'
      }
    ];
    // tags[0].Value = 'true';
    Tags.applyTags(tags, instanceId, function() {
      console.log('Instance', instanceId, 'reports status checks passed. Adding to pool.');
    });
  });
};

// Removes smake pool tags from the specified instance.
// The instanceId is passed to the callback function.
InstancePool.removeFromPool = function(instanceId, _done) {
  function done() { if (_done) { _done(); } }
  // var tags = POOL_TAGS;
  var tags = [
    {
      Key: SSHABLE_TAG.Key
    }
  ];
  // tags.push({ Key: SSHABLE_TAG.Key });
  Tags.removeTags(tags, instanceId, function() {
    done();
  });
};

// Assimilates an existing pool resource into a newly created pool.
InstancePool.assimilate = function(instance, _done) {
  function done() { if (_done) { _done(); } }

  // Take appropriate action based on ttl tag.
  Instances.handleTtl(instance);

  // Set 'sshable' tag as appropriate
  Instances.canSsh(instance.InstanceId, function(sshable) {
    if (sshable === true) {
      InstancePool.onSshable(instance.InstanceId);
    } else {
      InstancePool.startSshPolling(instance.InstanceId);
    }
  });
};

// Encapsluate in function to make looping easier
function create(instanceType) {
  InstancePool.create(instanceType, function(instanceId) {
    console.log('info', 'Starting new ' + instanceType + ' instance ' + instanceId);
  });
}

// Starts the instance pool. Examines all current EC2 instances.
// If there are existing pool resources, this function will
// assimilate them into the pooling strategy or terminate them,
// as necessary.
InstancePool.prototype.initialize = function(server, _done) {
  function done() { if (_done) { _done(); } }
  // Restart existing termination schedules (TTL's)
  this.getPoolInstancesByFilters(undefined, function(instances) {
    if (instances === undefined) {
      return done();
    }
    instances.forEach(function(instance) {
      Instances.handleTtl(instance);
    });
  });

  // Get a list of required instance types.
  var requiredInstances = this.poolingStrategy.getRequiredInstances();
  // Get a list of all current pool instances.
  this.getPoolInstances(function(currentInstances) {
    if (currentInstances === undefined) {
      if (server) { server.log('info', 'Did not find existing pool resources'); }
      // Create necessary instances.
      requiredInstances.forEach(function(requiredInstance) {
        var type = requiredInstance.type;
        var count = requiredInstance.count;
        for (var i = 0; i < count; i++) {
          InstancePool.create(type, function(instanceId) {
            if (server) { server.log('info', 'Starting new ' + type + ' instance ' + instanceId); }
          });
        }
      });
      done();
      return;
    }
    if (server) { server.log('info', 'Assimilating existing pool resources...'); }

    // Reschedule existing termination times.
    Object.keys(currentInstances).forEach(function(instanceType) {
      currentInstances[instanceType].forEach(function(instance) {
        InstancePool.assimilate(instance);
      });
    });

    // Compare required and current instance counts.
    var diff = {};
    // Below is an example diff object
    // {
    //   { 't1.micro': 3 } // Create three t1.micro instances
    //   { 't2.micro': -1} // Destroy one t2.micro instance
    //   ...
    // }
    requiredInstances.forEach(function(requiredInstance) {
      var type = requiredInstance.type;
      var required = requiredInstance.count;
      var currently = 0;
      if (currentInstances[type] !== undefined) {
        currently = currentInstances[type].length;
      }
      diff[type] = required - currently;
    });

    // Iterate over diff and create/destroy instances as necessary.
    Object.keys(diff).forEach(function(instanceType) {
      var typeDiff = diff[instanceType];

      // Destroy instances
      if (typeDiff < 0) {
        var destroyed = 0;
        currentInstances[instanceType].forEach(function(currentInstance) {
          if (destroyed++ < Math.abs(typeDiff)) {
            InstancePool.terminate(currentInstance.InstanceId, function(instanceId) {
              if (server) { server.log('info', 'Terminating ' + instanceType + ' instance ' + instanceId); }
            });
          }
        });
      }

      // Create instancess
      if (typeDiff > 0) {
        var created = 0;
        if (currentInstances[instanceType]) {
          created = currentInstances[instanceType].length;
        }
        var totalRequired = created + typeDiff;
        for (; created < totalRequired; created++) {
          create(instanceType);
        }
      }

      // If typeDiff == 0, do nothing
    });

    if (server) { server.log('info', 'Instance pool started successfully!'); }

    done();
  });
};

// Creates the specified number of instances.
// Specify creation details as follows:
// {
//   't1.micro': 1
// , 't2.micro': 3
//   ...
// }
InstancePool.createInstances = function(instances, callback) {
  Object.keys(instances).forEach(function(instanceType) {
    var typeDiff = instances[instanceType];

    // Create instancess
    if (typeDiff > 0) {
      for (var created = 0; created < typeDiff; created++) {
        var instanceId = InstancePool.create(instanceType, function(instanceId) {
          console.log('info', 'Starting new ' + instanceType + ' instance ' + instanceId);
        });
      }
    }

    // If typeDiff == 0, do nothing
  });
  if (callback) {
    callback();
  }
};

// Returns all instances that are currently in the instance pool,
// grouped by type. Example return object:
// {
//   't1.micro': [InstanceObject1, InstanceObject2, ...]
//   ...
// }
// These instances are not removed from the pool, nor is their
// formatting changed.
InstancePool.prototype.getPoolInstances = function(callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    if (instances === undefined) {
      callback(undefined);
    } else {
      var currentInstances = {};
      instances.forEach(function(instance) {
        var type = instance.InstanceType;
        var id = instance.InstanceId;
        // Define an object for the instance type if it doesn't already exist.
        if (currentInstances[type] === undefined) {
          currentInstances[type] = [];
        }

        // Add the instance to a list.
        currentInstances[type].push(instance);
      });
      callback(currentInstances);
    }
  });
};

// Returns pool instances, grouped by type.
// Output is formatted as follows:
// {
//   running: [id1, id2, id3, ...]
// , pending: [id4, id5, ...]
// }
InstancePool.prototype.getPoolInstancesByType = function(type, callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  , {
      Name: 'instance-type'
    , Values: [type]
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    callback(instances);
  });
};

// Returns all instances that match the specified filters.
// Note that this method may return instances that are not
// part of the instance pool.
InstancePool.prototype.getPoolInstancesByFilters = function(filters, callback) {
  // Refine filters
  var params = {};
  if (filters) {
    params.Filters = filters;
  }

  // Query EC2 for resources
  ec2.describeInstances(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
      callback(undefined);
    } else {
      var reservations = data.Reservations;
      if (!reservations) {
        callback(undefined);
        return;
      }
      if (!reservations[0]) {
        callback(undefined);
        return;
      }
      var instances = [];
      reservations.forEach(function(reservation) {
        reservation.Instances.forEach(function(instance) {
          instances.push(instance);
        });
      });

      callback(instances);
    }
  });
};

// Gets an instance of the specified type from the instance pool.
// The returned instance will no longer be part of the pool after calling
// this method. Its replacement will be handled by the pooling
// strategy specified when the instance pool was initialized.
InstancePool.prototype.getInstance = function(instanceSpec, callback) {
  this.getPoolInstancesByType(instanceSpec.type, function(instances) {
    // Organize the instances by state.
    // This will allow us to return pending instances if no
    // running instances are available.
    var instancesByState = {
      running: []
    , pending: []
    };
    instances.forEach(function(instance) {
      var type = instance.InstanceType;
      var id = instance.InstanceId;
      var state = instance.State.Name;
      var tags = instance.Tags;

      // Add the instanceId to a list depending on its state.
      instancesByState[state].push(instance);
    });

    // Choose an instance to return:
    var toReturn;
    // Need an instance that is running...
    if (instancesByState.running.length > 0) {
      // ..and that we can ssh into
      instancesByState.running.forEach(function(instance) {
        instance.Tags.forEach(function(tag) {
          if (tag.Key == 'sshable' && tag.Value == 'true') {
            toReturn = instance.InstanceId;
          }
        });
      });

      // If no instances are ready, stop
      if (toReturn === undefined) {
        console.log('Client requested', instanceSpec.type, 'instance, but one wasn\'t ready');
        return callback(undefined);
      }

    } else { // No running instances
      console.log('Client requested', instanceSpec.type, 'instance, but one wasn\'t ready');
      return callback(undefined);
    }

    // Apply TTL tags if specified
    if (instanceSpec.ttl !== undefined) {
      Instances.addTtl(toReturn, instanceSpec.ttl);
    }

    InstancePool.removeFromPool(toReturn, function() {
      poolingStrategy.notifyOfRemoval(instanceSpec.type, function(toCreate) {
        InstancePool.createInstances(toCreate);
      });
      callback(toReturn);
    });
  });
};

module.exports = InstancePool;
<commit_msg>Removed unnecessary print statement<commit_after>var request = require('request');
var AWS = require('aws-sdk');
var ec2 = new AWS.EC2({
  apiVersion: '2014-10-01'
, region: 'us-west-2'
});

var Instances = require('./Instances');
var Tags = require('./Tags');

var poolingStrategy;
module.exports.poolingStrategy = poolingStrategy;

const POOL_TAGS = [
  {
    Key: 'smake'
  , Value: 'pool'
  }
];
const SSHABLE_TAG = [
  {
    Key: 'sshable'
  , Value: 'false'
  }
];
const SSH_POLLING_INTERVAL = 30; // seconds

function InstancePool(strategy) {

  // Default to naive strategy.
  if (typeof(strategy) === 'undefined') {
    strategy = 'NaiveStrategy';
  }
  var _strategy = require('./poolingStrategies/' + strategy);
  this.poolingStrategy = new _strategy();
  poolingStrategy = new _strategy();
  this.ec2 = ec2;
}

// Returns the name of the pooling strategy currently in use
InstancePool.prototype.strategy = function(callback) {
  return this.poolingStrategy.constructor.name;
};

// Terminates the specified instance, interrupting any running
// jobs and destroying the attached EBS volume. Completion of
// this non-reversible process is signaled via the callback.
InstancePool.terminate = function(instanceId, callback) {
  var params = {
    InstanceIds: [instanceId]
  };
  ec2.terminateInstances(params, function(err, data) {
    if (err) {
      console.log(err, data);
    } else {
      callback(instanceId);
    }
  });
};

// Adds an instance of the specified type to the instance pool.
// Uses the default keypair. Completion signaled via callback.
InstancePool.create = function(type, _callback) {
  function callback(instanceId) {
    if (_callback) {
      _callback(instanceId);
    }
  }

  // Get ami list
  var amis = require('./defaultAmis.json');

  // Define instance paramaters
  var params = {
    ImageId: amis[type]
  , InstanceType: type
  , KeyName: 'smake'
  , MinCount: 1
  , MaxCount: 1
  };

  // Create the instance.
  ec2.runInstances(params, function(err, data) {
    if (err) {
      console.log('There was a problem creating the instance.', err);
      return;
    }

    // Apply pool tags to the new instance.
    var instanceId = data.Instances[0].InstanceId;
    var tags = POOL_TAGS;
    // tags.push(SSHABLE_TAG);
    Tags.applyTags(tags, instanceId, callback(instanceId));

    // tags = [];
    // tags.push(SSHABLE_TAG);
    Tags.applyTags(SSHABLE_TAG, instanceId, callback(instanceId));

    // We just notifed the caller that we were done, but there's a
    // bit more we should do. Doing it like this allows the caller
    // to continue chugging along while we wrap things up. This
    // enables the system to respond more quickly than it would otherwise.

    // Periodically check if the instance is accepting ssh connections.
    InstancePool.startSshPolling(instanceId);
  });
};

// Periodically check if it's possible to ssh into the instance.
// SSH_POLLING_INTERVAL determines the interval for polling.
// Checks immediately upon calling, and then at the specified interval.
InstancePool.startSshPolling = function(instanceId) {
  var pollingInterval = SSH_POLLING_INTERVAL * 1000; // Convert to ms
  var interval = setInterval(function() {
    Instances.canSsh(instanceId, function(sshable) {
      if (sshable) {
        InstancePool.onSshable(instanceId, interval);
      } else {
      }
    });
  }, pollingInterval);
};

// Called when it becomes possible to ssh into the instance.
// Sets the 'sshable' tag to 'true'
// Also cancels the setInterval function that was polling
// AWS to determine this.
InstancePool.onSshable = function(instanceId, interval) {
  if (interval) { clearInterval(interval); } // Stop polling.
  Tags.removeTags(SSHABLE_TAG, instanceId, function() {
    // var tags = SSHABLE_TAG.slice(0);
    // var tags = SSHABLE_TAG;
    var tags = [
      {
        Key: 'sshable'
      , Value: 'true'
      }
    ];
    // tags[0].Value = 'true';
    Tags.applyTags(tags, instanceId, function() {
      console.log('Instance', instanceId, 'reports status checks passed. Adding to pool.');
    });
  });
};

// Removes smake pool tags from the specified instance.
// The instanceId is passed to the callback function.
InstancePool.removeFromPool = function(instanceId, _done) {
  function done() { if (_done) { _done(); } }
  // var tags = POOL_TAGS;
  var tags = [
    {
      Key: SSHABLE_TAG.Key
    }
  ];
  // tags.push({ Key: SSHABLE_TAG.Key });
  Tags.removeTags(tags, instanceId, function() {
    done();
  });
};

// Assimilates an existing pool resource into a newly created pool.
InstancePool.assimilate = function(instance, _done) {
  function done() { if (_done) { _done(); } }

  // Take appropriate action based on ttl tag.
  Instances.handleTtl(instance);

  // Set 'sshable' tag as appropriate
  Instances.canSsh(instance.InstanceId, function(sshable) {
    if (sshable === true) {
      InstancePool.onSshable(instance.InstanceId);
    } else {
      InstancePool.startSshPolling(instance.InstanceId);
    }
  });
};

// Encapsluate in function to make looping easier
function create(instanceType) {
  InstancePool.create(instanceType, function(instanceId) {
    console.log('info', 'Starting new ' + instanceType + ' instance ' + instanceId);
  });
}

// Starts the instance pool. Examines all current EC2 instances.
// If there are existing pool resources, this function will
// assimilate them into the pooling strategy or terminate them,
// as necessary.
InstancePool.prototype.initialize = function(server, _done) {
  function done() { if (_done) { _done(); } }
  // Restart existing termination schedules (TTL's)
  this.getPoolInstancesByFilters(undefined, function(instances) {
    if (instances === undefined) {
      return done();
    }
    instances.forEach(function(instance) {
      Instances.handleTtl(instance);
    });
  });

  // Get a list of required instance types.
  var requiredInstances = this.poolingStrategy.getRequiredInstances();
  // Get a list of all current pool instances.
  this.getPoolInstances(function(currentInstances) {
    if (currentInstances === undefined) {
      if (server) { server.log('info', 'Did not find existing pool resources'); }
      // Create necessary instances.
      requiredInstances.forEach(function(requiredInstance) {
        var type = requiredInstance.type;
        var count = requiredInstance.count;
        for (var i = 0; i < count; i++) {
          InstancePool.create(type, function(instanceId) {
            if (server) { server.log('info', 'Starting new ' + type + ' instance ' + instanceId); }
          });
        }
      });
      done();
      return;
    }
    if (server) { server.log('info', 'Assimilating existing pool resources...'); }

    // Reschedule existing termination times.
    Object.keys(currentInstances).forEach(function(instanceType) {
      currentInstances[instanceType].forEach(function(instance) {
        InstancePool.assimilate(instance);
      });
    });

    // Compare required and current instance counts.
    var diff = {};
    // Below is an example diff object
    // {
    //   { 't1.micro': 3 } // Create three t1.micro instances
    //   { 't2.micro': -1} // Destroy one t2.micro instance
    //   ...
    // }
    requiredInstances.forEach(function(requiredInstance) {
      var type = requiredInstance.type;
      var required = requiredInstance.count;
      var currently = 0;
      if (currentInstances[type] !== undefined) {
        currently = currentInstances[type].length;
      }
      diff[type] = required - currently;
    });

    // Iterate over diff and create/destroy instances as necessary.
    Object.keys(diff).forEach(function(instanceType) {
      var typeDiff = diff[instanceType];

      // Destroy instances
      if (typeDiff < 0) {
        var destroyed = 0;
        currentInstances[instanceType].forEach(function(currentInstance) {
          if (destroyed++ < Math.abs(typeDiff)) {
            InstancePool.terminate(currentInstance.InstanceId, function(instanceId) {
              if (server) { server.log('info', 'Terminating ' + instanceType + ' instance ' + instanceId); }
            });
          }
        });
      }

      // Create instancess
      if (typeDiff > 0) {
        var created = 0;
        if (currentInstances[instanceType]) {
          created = currentInstances[instanceType].length;
        }
        var totalRequired = created + typeDiff;
        for (; created < totalRequired; created++) {
          create(instanceType);
        }
      }

      // If typeDiff == 0, do nothing
    });

    if (server) { server.log('info', 'Instance pool started successfully!'); }

    done();
  });
};

// Creates the specified number of instances.
// Specify creation details as follows:
// {
//   't1.micro': 1
// , 't2.micro': 3
//   ...
// }
InstancePool.createInstances = function(instances, callback) {
  Object.keys(instances).forEach(function(instanceType) {
    var typeDiff = instances[instanceType];

    // Create instancess
    if (typeDiff > 0) {
      for (var created = 0; created < typeDiff; created++) {
        var instanceId = InstancePool.create(instanceType, function(instanceId) {
          console.log('info', 'Starting new ' + instanceType + ' instance ' + instanceId);
        });
      }
    }

    // If typeDiff == 0, do nothing
  });
  if (callback) {
    callback();
  }
};

// Returns all instances that are currently in the instance pool,
// grouped by type. Example return object:
// {
//   't1.micro': [InstanceObject1, InstanceObject2, ...]
//   ...
// }
// These instances are not removed from the pool, nor is their
// formatting changed.
InstancePool.prototype.getPoolInstances = function(callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    if (instances === undefined) {
      callback(undefined);
    } else {
      var currentInstances = {};
      instances.forEach(function(instance) {
        var type = instance.InstanceType;
        var id = instance.InstanceId;
        // Define an object for the instance type if it doesn't already exist.
        if (currentInstances[type] === undefined) {
          currentInstances[type] = [];
        }

        // Add the instance to a list.
        currentInstances[type].push(instance);
      });
      callback(currentInstances);
    }
  });
};

// Returns pool instances, grouped by type.
// Output is formatted as follows:
// {
//   running: [id1, id2, id3, ...]
// , pending: [id4, id5, ...]
// }
InstancePool.prototype.getPoolInstancesByType = function(type, callback) {
  var filters = [
    {
      Name: 'tag-key'
    , Values: ['smake']
    }
  , {
      Name: 'tag-value'
    , Values: ['pool']
    }
  , {
      Name: 'instance-state-name'
    , Values: ['running', 'pending']
    }
  , {
      Name: 'instance-type'
    , Values: [type]
    }
  ];

  this.getPoolInstancesByFilters(filters, function(instances) {
    callback(instances);
  });
};

// Returns all instances that match the specified filters.
// Note that this method may return instances that are not
// part of the instance pool.
InstancePool.prototype.getPoolInstancesByFilters = function(filters, callback) {
  // Refine filters
  var params = {};
  if (filters) {
    params.Filters = filters;
  }

  // Query EC2 for resources
  ec2.describeInstances(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
      callback(undefined);
    } else {
      var reservations = data.Reservations;
      if (!reservations) {
        callback(undefined);
        return;
      }
      if (!reservations[0]) {
        callback(undefined);
        return;
      }
      var instances = [];
      reservations.forEach(function(reservation) {
        reservation.Instances.forEach(function(instance) {
          instances.push(instance);
        });
      });

      callback(instances);
    }
  });
};

// Gets an instance of the specified type from the instance pool.
// The returned instance will no longer be part of the pool after calling
// this method. Its replacement will be handled by the pooling
// strategy specified when the instance pool was initialized.
InstancePool.prototype.getInstance = function(instanceSpec, callback) {
  this.getPoolInstancesByType(instanceSpec.type, function(instances) {
    // Organize the instances by state.
    // This will allow us to return pending instances if no
    // running instances are available.
    var instancesByState = {
      running: []
    , pending: []
    };
    instances.forEach(function(instance) {
      var type = instance.InstanceType;
      var id = instance.InstanceId;
      var state = instance.State.Name;
      var tags = instance.Tags;

      // Add the instanceId to a list depending on its state.
      instancesByState[state].push(instance);
    });

    // Choose an instance to return:
    var toReturn;
    // Need an instance that is running...
    if (instancesByState.running.length > 0) {
      // ..and that we can ssh into
      instancesByState.running.forEach(function(instance) {
        instance.Tags.forEach(function(tag) {
          if (tag.Key == 'sshable' && tag.Value == 'true') {
            toReturn = instance.InstanceId;
          }
        });
      });

      // If no instances are ready, stop
      if (toReturn === undefined) {
        console.log('Client requested', instanceSpec.type, 'instance, but one wasn\'t ready');
        return callback(undefined);
      }

    } else { // No running instances
      console.log('Client requested', instanceSpec.type, 'instance, but one wasn\'t ready');
      return callback(undefined);
    }

    // Apply TTL tags if specified
    if (instanceSpec.ttl !== undefined) {
      Instances.addTtl(toReturn, instanceSpec.ttl);
    }

    InstancePool.removeFromPool(toReturn, function() {
      poolingStrategy.notifyOfRemoval(instanceSpec.type, function(toCreate) {
        InstancePool.createInstances(toCreate);
      });
      callback(toReturn);
    });
  });
};

module.exports = InstancePool;
<|endoftext|>